Initial commit with inital CMS
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
using System.Text.Json;
|
||||
using FluentAssertions;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NSubstitute;
|
||||
using SlpModularCms.Core.Exceptions;
|
||||
using Xunit;
|
||||
|
||||
namespace SlpModularCms.Core.Tests.Exceptions;
|
||||
|
||||
public class GlobalExceptionHandlerTests
|
||||
{
|
||||
private readonly ILogger<GlobalExceptionHandler> _logger;
|
||||
private readonly IHostEnvironment _env;
|
||||
private readonly GlobalExceptionHandler _handler;
|
||||
|
||||
public GlobalExceptionHandlerTests()
|
||||
{
|
||||
_logger = Substitute.For<ILogger<GlobalExceptionHandler>>();
|
||||
_env = Substitute.For<IHostEnvironment>();
|
||||
_handler = new GlobalExceptionHandler(_logger, _env);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TryHandleAsync_ShouldReturnInternalServerError_ForGenericException()
|
||||
{
|
||||
// Arrange
|
||||
var context = new DefaultHttpContext();
|
||||
context.Response.Body = new MemoryStream();
|
||||
var exception = new Exception("Test error");
|
||||
var cancellationToken = CancellationToken.None;
|
||||
|
||||
_env.EnvironmentName.Returns("Production");
|
||||
|
||||
// Act
|
||||
var result = await _handler.TryHandleAsync(context, exception, cancellationToken);
|
||||
|
||||
// Assert
|
||||
result.Should().BeTrue();
|
||||
context.Response.StatusCode.Should().Be(StatusCodes.Status500InternalServerError);
|
||||
|
||||
context.Response.Body.Seek(0, SeekOrigin.Begin);
|
||||
using var reader = new StreamReader(context.Response.Body);
|
||||
var body = await reader.ReadToEndAsync();
|
||||
var response = JsonSerializer.Deserialize<ApiErrorResponse>(body, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
|
||||
|
||||
response.Should().NotBeNull();
|
||||
response!.Message.Should().Be("Er is een interne serverfout opgetreden.");
|
||||
response.Detail.Should().BeNull();
|
||||
response.TraceId.Should().NotBeNullOrEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TryHandleAsync_ShouldIncludeDetails_WhenInDevelopment()
|
||||
{
|
||||
// Arrange
|
||||
var context = new DefaultHttpContext();
|
||||
context.Response.Body = new MemoryStream();
|
||||
var exception = new Exception("Test error");
|
||||
var cancellationToken = CancellationToken.None;
|
||||
|
||||
_env.EnvironmentName.Returns("Development");
|
||||
|
||||
// Act
|
||||
await _handler.TryHandleAsync(context, exception, cancellationToken);
|
||||
|
||||
// Assert
|
||||
context.Response.Body.Seek(0, SeekOrigin.Begin);
|
||||
using var reader = new StreamReader(context.Response.Body);
|
||||
var body = await reader.ReadToEndAsync();
|
||||
var response = JsonSerializer.Deserialize<ApiErrorResponse>(body, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
|
||||
|
||||
response!.Detail.Should().Contain("Test error");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
using System.Security.Claims;
|
||||
using FluentAssertions;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using SlpModularCms.Core.Identity.Authorization;
|
||||
using Xunit;
|
||||
|
||||
namespace SlpModularCms.Core.Tests.Identity;
|
||||
|
||||
public class HierarchicalRoleHandlerTests
|
||||
{
|
||||
private readonly HierarchicalRoleHandler _handler;
|
||||
|
||||
public HierarchicalRoleHandlerTests()
|
||||
{
|
||||
_handler = new HierarchicalRoleHandler();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("Owner", "Owner", true)]
|
||||
[InlineData("Owner", "Administrator", true)]
|
||||
[InlineData("Owner", "User", true)]
|
||||
[InlineData("Administrator", "Administrator", true)]
|
||||
[InlineData("Administrator", "User", true)]
|
||||
[InlineData("Administrator", "Owner", false)]
|
||||
[InlineData("User", "User", true)]
|
||||
[InlineData("User", "Administrator", false)]
|
||||
[InlineData("User", "Owner", false)]
|
||||
public async Task HandleAsync_ShouldValidateHierarchyCorrectly(string userRole, string requiredRole, bool shouldSucceed)
|
||||
{
|
||||
// Arrange
|
||||
var requirement = new HierarchicalRoleRequirement(requiredRole);
|
||||
var user = new ClaimsPrincipal(new ClaimsIdentity(new[]
|
||||
{
|
||||
new Claim(ClaimTypes.Role, userRole)
|
||||
}));
|
||||
|
||||
var context = new AuthorizationHandlerContext(new[] { requirement }, user, null);
|
||||
|
||||
// Act
|
||||
await _handler.HandleAsync(context);
|
||||
|
||||
// Assert
|
||||
if (shouldSucceed)
|
||||
{
|
||||
context.HasSucceeded.Should().BeTrue();
|
||||
}
|
||||
else
|
||||
{
|
||||
context.HasSucceeded.Should().BeFalse();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NSubstitute;
|
||||
using SlpModularCms.Core.Data;
|
||||
using SlpModularCms.Core.Identity.Entities;
|
||||
using SlpModularCms.Core.Identity.Services;
|
||||
using SlpModularCms.Core.Exceptions;
|
||||
using Xunit;
|
||||
using FluentAssertions;
|
||||
|
||||
namespace SlpModularCms.Core.Tests.Identity;
|
||||
|
||||
public class InvitationServiceTests
|
||||
{
|
||||
private readonly ApplicationDbContext _context;
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly InvitationService _service;
|
||||
|
||||
public InvitationServiceTests()
|
||||
{
|
||||
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
|
||||
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
|
||||
.Options;
|
||||
_context = new ApplicationDbContext(options);
|
||||
|
||||
var store = Substitute.For<IUserStore<ApplicationUser>>();
|
||||
_userManager = Substitute.For<UserManager<ApplicationUser>>(store, null, null, null, null, null, null, null, null);
|
||||
|
||||
_service = new InvitationService(_context, _userManager);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateInvitationAsync_ShouldCreateRecordInDb()
|
||||
{
|
||||
// Arrange
|
||||
var email = "test@example.com";
|
||||
var role = "User";
|
||||
|
||||
// Act
|
||||
var token = await _service.CreateInvitationAsync(email, role);
|
||||
|
||||
// Assert
|
||||
token.Should().NotBeNullOrEmpty();
|
||||
var invitation = await _context.Invitations.FirstOrDefaultAsync(i => i.Token == token);
|
||||
invitation.Should().NotBeNull();
|
||||
invitation!.Email.Should().Be(email);
|
||||
invitation.Role.Should().Be(role);
|
||||
invitation.IsAccepted.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ValidateInvitationAsync_ShouldReturnTrue_ForValidToken()
|
||||
{
|
||||
// Arrange
|
||||
var token = await _service.CreateInvitationAsync("test@example.com", "User");
|
||||
|
||||
// Act
|
||||
var isValid = await _service.ValidateInvitationAsync(token);
|
||||
|
||||
// Assert
|
||||
isValid.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CompleteInvitationAsync_ShouldCreateUserAndMarkAsAccepted()
|
||||
{
|
||||
// Arrange
|
||||
var email = "test@example.com";
|
||||
var role = "User";
|
||||
var password = "SafePassword123!";
|
||||
var token = await _service.CreateInvitationAsync(email, role);
|
||||
|
||||
_userManager.CreateAsync(Arg.Any<ApplicationUser>(), password)
|
||||
.Returns(IdentityResult.Success);
|
||||
_userManager.AddToRoleAsync(Arg.Any<ApplicationUser>(), role)
|
||||
.Returns(IdentityResult.Success);
|
||||
|
||||
// Act
|
||||
await _service.CompleteInvitationAsync(token, password);
|
||||
|
||||
// Assert
|
||||
var invitation = await _context.Invitations.FirstOrDefaultAsync(i => i.Token == token);
|
||||
invitation!.IsAccepted.Should().BeTrue();
|
||||
await _userManager.Received(1).CreateAsync(Arg.Is<ApplicationUser>(u => u.Email == email), password);
|
||||
await _userManager.Received(1).AddToRoleAsync(Arg.Any<ApplicationUser>(), role);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CompleteInvitationAsync_ShouldThrow_WhenTokenInvalid()
|
||||
{
|
||||
// Act
|
||||
var act = () => _service.CompleteInvitationAsync("invalid-token", "password");
|
||||
|
||||
// Assert
|
||||
await act.Should().ThrowAsync<UnauthorizedException>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AutoFixture" Version="4.18.1" />
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.4" />
|
||||
<PackageReference Include="FluentAssertions" Version="8.10.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="10.0.9" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="10.0.9" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
|
||||
<PackageReference Include="NSubstitute" Version="5.3.0" />
|
||||
<PackageReference Include="xunit" Version="2.9.3" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Using Include="Xunit" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\SlpModularCms.Core\SlpModularCms.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Reference in New Issue
Block a user