Renamed test projects

This commit is contained in:
2024-12-10 16:39:28 +01:00
parent 4d91ce1819
commit ee7bf1e204
21 changed files with 31 additions and 27 deletions

View File

@@ -0,0 +1,95 @@
using System.Security.Claims;
using HopFrame.Database.Models;
using HopFrame.Database.Repositories;
using HopFrame.Security.Claims;
using HopFrame.Web;
using HopFrame.Web.Services;
using Microsoft.AspNetCore.Http;
using Moq;
namespace HopFrame.Tests.Web;
public class AuthMiddlewareTests {
private readonly RequestDelegate _delegate = _ => Task.CompletedTask;
public AuthMiddleware SetupEnvironment(bool isLoggedIn = true, Token newToken = null) {
var auth = new Mock<IAuthService>();
auth
.Setup(a => a.IsLoggedIn())
.ReturnsAsync(isLoggedIn);
auth
.Setup(a => a.RefreshLogin())
.ReturnsAsync(newToken);
var perms = new Mock<IPermissionRepository>();
perms
.Setup(p => p.GetFullPermissions(It.Is<User>(u => newToken.Owner.Id == u.Id)))
.ReturnsAsync(CreateDummyUser().Permissions.Select(p => p.PermissionName).ToList);
return new AuthMiddleware(auth.Object, perms.Object);
}
private User CreateDummyUser() => new() {
Id = Guid.NewGuid(),
CreatedAt = DateTime.Now,
Email = "test@example.com",
Username = "ExampleUser",
Password = "1234567890",
Permissions = new List<Permission> {
new () {
PermissionName = "test.permission"
}
}
};
[Fact]
public async Task InvokeAsync_With_ValidLogin_Should_Succeed() {
// Arrange
var auth = SetupEnvironment();
var context = new DefaultHttpContext();
// Act
await auth.InvokeAsync(context, _delegate);
// Assert
Assert.Null(context.User.FindFirst(HopFrameClaimTypes.UserId));
Assert.Null(context.User.FindFirst(HopFrameClaimTypes.AccessTokenId));
Assert.Null(context.User.FindFirst(HopFrameClaimTypes.Permission));
}
[Fact]
public async Task InvokeAsync_With_InvalidLoginValidToken_Should_Succeed() {
// Arrange
var token = new Token {
Content = Guid.NewGuid(),
CreatedAt = DateTime.Now,
Type = Token.AccessTokenType,
Owner = CreateDummyUser()
};
var auth = SetupEnvironment(false, token);
var context = new DefaultHttpContext();
// Act
await auth.InvokeAsync(context, _delegate);
// Assert
Assert.Equal(token.Owner.Id.ToString(), context.User.FindFirstValue(HopFrameClaimTypes.UserId));
Assert.Equal(token.Content.ToString(), context.User.FindFirstValue(HopFrameClaimTypes.AccessTokenId));
Assert.Equal(token.Owner.Permissions.First().PermissionName, context.User.FindFirstValue(HopFrameClaimTypes.Permission));
}
[Fact]
public async Task InvokeAsync_With_InvalidLoginInvalidToken_Should_Succeed() {
// Arrange
var auth = SetupEnvironment(false);
var context = new DefaultHttpContext();
// Act
await auth.InvokeAsync(context, _delegate);
// Assert
Assert.Null(context.User.FindFirst(HopFrameClaimTypes.UserId));
Assert.Null(context.User.FindFirst(HopFrameClaimTypes.AccessTokenId));
Assert.Null(context.User.FindFirst(HopFrameClaimTypes.Permission));
}
}

View File

@@ -0,0 +1,334 @@
using HopFrame.Database.Models;
using HopFrame.Database.Repositories;
using HopFrame.Security.Claims;
using HopFrame.Security.Models;
using HopFrame.Tests.Web.Extensions;
using HopFrame.Web.Services;
using HopFrame.Web.Services.Implementation;
using Microsoft.AspNetCore.Http;
using Moq;
namespace HopFrame.Tests.Web;
public class AuthServiceTests {
private readonly Guid _refreshToken = Guid.NewGuid();
private readonly Guid _accessToken = Guid.NewGuid();
private (IAuthService, HttpContext) SetupEnvironment(bool passwordIsCorrect = true, Token providedRefreshToken = null, string providedTokenCookie = null, Token providedAccessToken = null) {
var accessor = new HttpContextAccessor {
HttpContext = new DefaultHttpContext()
};
if (providedTokenCookie != null) {
var cookies = new Mock<IRequestCookieCollection>();
cookies
.SetupGet(c => c[ITokenContext.RefreshTokenType])
.Returns(providedTokenCookie);
accessor.HttpContext.Request.Cookies = cookies.Object;
}
var users = new Mock<IUserRepository>();
users
.Setup(u => u.GetUserByEmail(It.Is<string>(email => CreateDummyUser().Email == email)))
.ReturnsAsync(CreateDummyUser());
users
.Setup(u => u.CheckUserPassword(It.Is<User>(u => u.Email == CreateDummyUser().Email), It.IsAny<string>()))
.ReturnsAsync(passwordIsCorrect);
users
.Setup(u => u.AddUser(It.IsAny<User>()))
.ReturnsAsync(CreateDummyUser());
users
.Setup(u => u.GetUsers())
.ReturnsAsync(new List<User> { CreateDummyUser() });
var tokens = new Mock<ITokenRepository>();
tokens
.Setup(t => t.CreateToken(It.Is<int>(t => t == Token.RefreshTokenType), It.IsAny<User>()))
.ReturnsAsync(new Token {
Content = _refreshToken,
Type = Token.RefreshTokenType
});
tokens
.Setup(t => t.CreateToken(It.Is<int>(t => t == Token.AccessTokenType), It.IsAny<User>()))
.ReturnsAsync(new Token {
Content = _accessToken,
Type = Token.AccessTokenType
});
tokens
.Setup(t => t.GetToken(It.Is<string>(token => token == _refreshToken.ToString())))
.ReturnsAsync(providedRefreshToken);
var context = new Mock<ITokenContext>();
context
.Setup(c => c.User)
.Returns(CreateDummyUser());
context
.Setup(c => c.AccessToken)
.Returns(providedAccessToken);
return (new AuthService(users.Object, accessor, tokens.Object, context.Object), accessor.HttpContext);
}
private User CreateDummyUser() => new() {
Id = Guid.NewGuid(),
CreatedAt = DateTime.Now,
Email = "test@example.com",
Username = "ExampleUser",
Password = "1234567890"
};
[Fact]
public async Task Register_Should_Succeed() {
// Arrange
var (service, context) = SetupEnvironment();
var register = new UserRegister {
Email = CreateDummyUser().Email,
Username = CreateDummyUser().Username,
Password = CreateDummyUser().Password
};
// Act
await service.Register(register);
// Assert
Assert.Equal(_accessToken.ToString(), context.Response.Headers.FindCookie(ITokenContext.AccessTokenType));
Assert.Equal(_refreshToken.ToString(), context.Response.Headers.FindCookie(ITokenContext.RefreshTokenType));
}
[Fact]
public async Task Login_Should_Succeed() {
// Arrange
var (service, context) = SetupEnvironment();
var login = new UserLogin {
Email = CreateDummyUser().Email,
Password = CreateDummyUser().Password
};
// Act
var result = await service.Login(login);
// Assert
Assert.True(result);
Assert.Equal(_accessToken.ToString(), context.Response.Headers.FindCookie(ITokenContext.AccessTokenType));
Assert.Equal(_refreshToken.ToString(), context.Response.Headers.FindCookie(ITokenContext.RefreshTokenType));
}
[Fact]
public async Task Login_With_WrongPassword_Should_Fail() {
// Arrange
var (service, context) = SetupEnvironment(false);
var login = new UserLogin {
Email = CreateDummyUser().Email,
Password = CreateDummyUser().Password
};
// Act
var result = await service.Login(login);
// Assert
Assert.False(result);
Assert.Null(context.Response.Headers.FindCookie(ITokenContext.AccessTokenType));
Assert.Null(context.Response.Headers.FindCookie(ITokenContext.RefreshTokenType));
}
[Fact]
public async Task Login_With_WrongEmail_Should_Fail() {
// Arrange
var (service, context) = SetupEnvironment();
var login = new UserLogin {
Email = "wrong@example.com",
Password = CreateDummyUser().Password
};
// Act
var result = await service.Login(login);
// Assert
Assert.False(result);
Assert.Null(context.Response.Headers.FindCookie(ITokenContext.RefreshTokenType));
Assert.Null(context.Response.Headers.FindCookie(ITokenContext.AccessTokenType));
}
[Fact]
public async Task Logout_Should_Succeed() {
// Arrange
var (service, context) = SetupEnvironment(providedTokenCookie: _refreshToken.ToString());
context.Response.Cookies.Append(ITokenContext.AccessTokenType, _accessToken.ToString());
context.Response.Cookies.Append(ITokenContext.RefreshTokenType, _refreshToken.ToString());
// Act
await service.Logout();
// Assert
Assert.Null(context.Response.Headers.FindCookie(ITokenContext.AccessTokenType));
Assert.Null(context.Response.Headers.FindCookie(ITokenContext.RefreshTokenType));
}
[Fact]
public async Task RefreshLogin_Should_Succeed() {
// Arrange
var token = new Token {
Type = Token.RefreshTokenType,
Content = _refreshToken,
CreatedAt = DateTime.Now,
Owner = CreateDummyUser()
};
var (service, context) = SetupEnvironment(true, token, token.Content.ToString());
// Act
var result = await service.RefreshLogin();
// Assert
Assert.NotNull(result);
Assert.Equal(_accessToken, result.Content);
Assert.Equal(_accessToken.ToString(), context.Response.Headers.FindCookie(ITokenContext.AccessTokenType));
}
[Fact]
public async Task RefreshLogin_With_NoProvidedToken_Should_Fail() {
// Arrange
var (service, context) = SetupEnvironment();
// Act
var result = await service.RefreshLogin();
// Assert
Assert.Null(result);
Assert.Null(context.Response.Headers.FindCookie(ITokenContext.AccessTokenType));
}
[Fact]
public async Task RefreshLogin_With_WrongToken_Should_Fail() {
// Arrange
var (service, context) = SetupEnvironment(true, null, _refreshToken.ToString());
// Act
var result = await service.RefreshLogin();
// Assert
Assert.Null(result);
Assert.Null(context.Response.Headers.FindCookie(ITokenContext.AccessTokenType));
}
[Fact]
public async Task RefreshLogin_With_WrongTokenType_Should_Fail() {
// Arrange
var token = new Token {
Type = Token.AccessTokenType,
Content = _refreshToken,
CreatedAt = DateTime.Now,
Owner = CreateDummyUser()
};
var (service, context) = SetupEnvironment(true, token, token.Content.ToString());
// Act
var result = await service.RefreshLogin();
// Assert
Assert.Null(result);
Assert.Null(context.Response.Headers.FindCookie(ITokenContext.AccessTokenType));
}
[Fact]
public async Task RefreshLogin_With_ExpiredToken_Should_Fail() {
// Arrange
var token = new Token {
Type = Token.RefreshTokenType,
Content = _refreshToken,
CreatedAt = DateTime.MinValue,
Owner = CreateDummyUser()
};
var (service, context) = SetupEnvironment(true, token, token.Content.ToString());
// Act
var result = await service.RefreshLogin();
// Assert
Assert.Null(result);
Assert.Null(context.Response.Headers.FindCookie(ITokenContext.AccessTokenType));
}
[Fact]
public async Task IsLoggedIn_Should_Succeed() {
// Arrange
var token = new Token {
Type = Token.AccessTokenType,
Content = _accessToken,
CreatedAt = DateTime.Now,
Owner = CreateDummyUser()
};
var (service, context) = SetupEnvironment(providedAccessToken: token);
// Act
var result = await service.IsLoggedIn();
// Assert
Assert.True(result);
}
[Fact]
public async Task IsLoggedIn_With_NoProvidedToken_Should_Fail() {
// Arrange
var (service, context) = SetupEnvironment();
// Act
var result = await service.IsLoggedIn();
// Assert
Assert.False(result);
}
[Fact]
public async Task IsLoggedIn_With_WrongTokenType_Should_Fail() {
// Arrange
var token = new Token {
Type = Token.RefreshTokenType,
Content = _accessToken,
CreatedAt = DateTime.Now,
Owner = CreateDummyUser()
};
var (service, context) = SetupEnvironment(providedAccessToken: token);
// Act
var result = await service.IsLoggedIn();
// Assert
Assert.False(result);
}
[Fact]
public async Task IsLoggedIn_With_ExpiredToken_Should_Fail() {
// Arrange
var token = new Token {
Type = Token.AccessTokenType,
Content = _accessToken,
CreatedAt = DateTime.MinValue,
Owner = CreateDummyUser()
};
var (service, context) = SetupEnvironment(providedAccessToken: token);
// Act
var result = await service.IsLoggedIn();
// Assert
Assert.False(result);
}
[Fact]
public async Task IsLoggedIn_With_NoOwner_Should_Fail() {
// Arrange
var token = new Token {
Type = Token.AccessTokenType,
Content = _accessToken,
CreatedAt = DateTime.Now,
Owner = null
};
var (service, context) = SetupEnvironment(providedAccessToken: token);
// Act
var result = await service.IsLoggedIn();
// Assert
Assert.False(result);
}
}

View File

@@ -0,0 +1,29 @@
using System.Web;
using Microsoft.AspNetCore.Http;
namespace HopFrame.Tests.Web.Extensions;
internal static class HttpContextExtensions {
/// <summary>Extracts the partial cookie value from the header section.</summary>
/// <param name="headers"><inheritdoc cref="IHeaderDictionary" path="/summary"/></param>
/// <param name="key">The key for identifying the cookie.</param>
/// <returns>The value of the cookie.</returns>
public static string FindCookie(this IHeaderDictionary headers, string key)
{
string headerKey = $"{key}=";
var cookies = headers.Values
.SelectMany(h => h)
.Where(header => header.StartsWith(headerKey))
.Select(header => header.Substring(headerKey.Length).Split(';').First())
.ToArray();
//Note: cookie values in a header are encoded like a uri parameter value.
var value = cookies.LastOrDefault();//and the last set value, is the relevant one.
if (string.IsNullOrEmpty(value))
return null;
//That's why we should decode that last value, before we return it.
var decoded = HttpUtility.UrlDecode(value);
return decoded;
}
}

View File

@@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>disable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.0"/>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0"/>
<PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="xunit" Version="2.5.3"/>
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3"/>
</ItemGroup>
<ItemGroup>
<Using Include="Xunit"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\HopFrame.Web\HopFrame.Web.csproj" />
</ItemGroup>
</Project>