Initial commit with inital CMS

This commit is contained in:
2026-06-15 17:00:16 +02:00
commit 95d986790e
132 changed files with 7624 additions and 0 deletions
@@ -0,0 +1,68 @@
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging.Abstractions;
using NSubstitute;
using SlpModularCms.Core.Availability;
using SlpModularCms.Modules.Availability.Middleware;
using Xunit;
using FluentAssertions;
namespace SlpModularCms.Modules.Availability.Tests;
public class AvailabilityMiddlewareTests
{
private readonly IAvailabilityService _service;
private readonly AvailabilityMiddleware _middleware;
private readonly RequestDelegate _next;
public AvailabilityMiddlewareTests()
{
_service = Substitute.For<IAvailabilityService>();
_next = Substitute.For<RequestDelegate>();
_middleware = new AvailabilityMiddleware(_next, NullLogger<AvailabilityMiddleware>.Instance);
}
[Fact]
public async Task InvokeAsync_ShouldAllowRequest_WhenSystemAvailable()
{
// Arrange
var context = new DefaultHttpContext();
_service.IsAvailableAsync().Returns(AvailabilityStatus.Available);
// Act
await _middleware.InvokeAsync(context, _service);
// Assert
await _next.Received(1).Invoke(context);
}
[Fact]
public async Task InvokeAsync_ShouldBlockRequest_WhenSystemUnavailable()
{
// Arrange
var context = new DefaultHttpContext();
context.Response.Body = new MemoryStream();
_service.IsAvailableAsync().Returns(AvailabilityStatus.NotAvailable);
// Act
await _middleware.InvokeAsync(context, _service);
// Assert
await _next.DidNotReceive().Invoke(Arg.Any<HttpContext>());
context.Response.StatusCode.Should().Be(StatusCodes.Status503ServiceUnavailable);
}
[Fact]
public async Task InvokeAsync_ShouldAllowBypass_ForStatusEndpoint()
{
// Arrange
var context = new DefaultHttpContext();
context.Request.Path = "/api/availability/status";
_service.IsAvailableAsync().Returns(AvailabilityStatus.NotAvailable);
// Act
await _middleware.InvokeAsync(context, _service);
// Assert
await _next.Received(1).Invoke(context);
}
}
@@ -0,0 +1,77 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using SlpModularCms.Core.Availability;
using SlpModularCms.Core.Data;
using SlpModularCms.Core.Identity.Entities;
using SlpModularCms.Modules.Availability.Services;
using Xunit;
using FluentAssertions;
using NSubstitute;
namespace SlpModularCms.Modules.Availability.Tests;
public class PersistentAvailabilityServiceTests
{
private readonly ApplicationDbContext _context;
private readonly PersistentAvailabilityService _service;
public PersistentAvailabilityServiceTests()
{
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
.Options;
_context = new ApplicationDbContext(options);
var availabilityOptions = Substitute.For<IOptions<AvailabilityOptions>>();
availabilityOptions.Value.Returns(new AvailabilityOptions { CircuitBreakerSeconds = 30 });
_service = new PersistentAvailabilityService(_context, availabilityOptions);
}
[Fact]
public async Task IsAvailableAsync_ShouldReturnAvailable_WhenNoStateInDb()
{
// Act
var status = await _service.IsAvailableAsync();
// Assert
status.Should().Be(AvailabilityStatus.Available);
}
[Fact]
public async Task UpdateStatusAsync_ShouldPersistInDb()
{
// Act
await _service.UpdateStatusAsync(AvailabilityStatus.Maintenance, "Maintenance mode", "Admin");
// Assert
var status = await _service.IsAvailableAsync();
status.Should().Be(AvailabilityStatus.Maintenance);
var dbState = await _context.AvailabilityStates.FirstAsync();
dbState.Status.Should().Be(AvailabilityStatus.Maintenance);
dbState.Message.Should().Be("Maintenance mode");
dbState.UpdatedBy.Should().Be("Admin");
}
[Fact]
public async Task CircuitBreaker_ShouldReturnCachedStatus_AfterDatabaseError()
{
// Deze test is lastig met in-memory DB omdat deze zelden 'failt'.
// Maar we kunnen de logica testen door de context te disposen (simulatie van DB error).
// Arrange
await _service.UpdateStatusAsync(AvailabilityStatus.Available, "Reset", "System");
_context.Dispose(); // Forceer error op volgende DB call
// Act
var status1 = await _service.IsAvailableAsync(); // Deze zou moeten failen en circuit breaker activeren
// Assert
status1.Should().Be(AvailabilityStatus.Available); // Fallback status
// Act 2: Circuit breaker is nu actief
var status2 = await _service.IsAvailableAsync();
status2.Should().Be(AvailabilityStatus.Available); // Direct uit cache
}
}
@@ -0,0 +1,31 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.4" />
<PackageReference Include="FluentAssertions" Version="8.10.0" />
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.3.11" />
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="10.0.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="10.0.9" />
<PackageReference Include="Microsoft.Extensions.Logging.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.Modules.Availability\SlpModularCms.Modules.Availability.csproj" />
</ItemGroup>
</Project>