Initial curated itworx-knowledge-hub source from 3b1751d0d9dee551aa6994eee0093704dc334817
This commit is contained in:
commit
ad8ad252a9
825 files changed
+124216
No files matched your search
@@ -0,0 +1,134 @@
|
||||
using ItWorx.KnowledgeHub.Infrastructure.Operations;
|
||||
using ItWorx.KnowledgeHub.Infrastructure.Persistence;
|
||||
using ItWorx.KnowledgeHub.IntegrationTests.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Diagnostics.HealthChecks;
|
||||
|
||||
namespace ItWorx.KnowledgeHub.IntegrationTests.Operations;
|
||||
|
||||
/// <summary>Proves M7-A05 ("Health endpoints distinguish liveness, readiness and
|
||||
/// degraded dependencies") against real PostgreSQL and a real filesystem, not mocks.
|
||||
/// </summary>
|
||||
[Collection(PostgresTestGroup.Name)]
|
||||
public sealed class HealthCheckTests(PostgresContainerFixture postgres)
|
||||
{
|
||||
[Fact]
|
||||
public async Task A_fully_migrated_reachable_database_is_healthy()
|
||||
{
|
||||
await using var target = await RestoreTargetDatabase.CreateAsync(postgres);
|
||||
var options = new DbContextOptionsBuilder<KnowledgeHubDbContext>()
|
||||
.UseNpgsql(target.ConnectionString, npgsql => npgsql.MigrationsAssembly(typeof(KnowledgeHubDbContext).Assembly.GetName().Name))
|
||||
.UseSnakeCaseNamingConvention()
|
||||
.Options;
|
||||
await using var db = new KnowledgeHubDbContext(options);
|
||||
var check = new DatabaseHealthCheck(db);
|
||||
|
||||
var result = await check.CheckHealthAsync(new HealthCheckContext());
|
||||
|
||||
Assert.Equal(HealthStatus.Healthy, result.Status);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task An_unreachable_database_is_unhealthy_not_a_thrown_exception()
|
||||
{
|
||||
var options = new DbContextOptionsBuilder<KnowledgeHubDbContext>()
|
||||
.UseNpgsql("Host=127.0.0.1;Port=1;Database=does-not-exist;Username=nobody;Password=nothing;Timeout=1")
|
||||
.UseSnakeCaseNamingConvention()
|
||||
.Options;
|
||||
await using var db = new KnowledgeHubDbContext(options);
|
||||
var check = new DatabaseHealthCheck(db);
|
||||
|
||||
var result = await check.CheckHealthAsync(new HealthCheckContext());
|
||||
|
||||
Assert.Equal(HealthStatus.Unhealthy, result.Status);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task A_database_with_pending_migrations_is_unhealthy()
|
||||
{
|
||||
var databaseName = $"health_unmigrated_{Guid.NewGuid():N}";
|
||||
await using (var admin = new Npgsql.NpgsqlConnection(postgres.AdminConnectionString))
|
||||
{
|
||||
await admin.OpenAsync();
|
||||
await using var create = new Npgsql.NpgsqlCommand($"CREATE DATABASE \"{databaseName}\"", admin);
|
||||
await create.ExecuteNonQueryAsync();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var connectionString = new Npgsql.NpgsqlConnectionStringBuilder(postgres.AdminConnectionString) { Database = databaseName }.ConnectionString;
|
||||
var options = new DbContextOptionsBuilder<KnowledgeHubDbContext>()
|
||||
.UseNpgsql(connectionString, npgsql => npgsql.MigrationsAssembly(typeof(KnowledgeHubDbContext).Assembly.GetName().Name))
|
||||
.UseSnakeCaseNamingConvention()
|
||||
.Options;
|
||||
await using var db = new KnowledgeHubDbContext(options);
|
||||
var check = new DatabaseHealthCheck(db);
|
||||
|
||||
// Reachable, but never migrated — must never be reported ready.
|
||||
var result = await check.CheckHealthAsync(new HealthCheckContext());
|
||||
|
||||
Assert.Equal(HealthStatus.Unhealthy, result.Status);
|
||||
}
|
||||
finally
|
||||
{
|
||||
await using var admin = new Npgsql.NpgsqlConnection(postgres.AdminConnectionString);
|
||||
await admin.OpenAsync();
|
||||
await using var drop = new Npgsql.NpgsqlCommand($"DROP DATABASE IF EXISTS \"{databaseName}\" WITH (FORCE)", admin);
|
||||
await drop.ExecuteNonQueryAsync();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task All_writable_configured_paths_are_healthy()
|
||||
{
|
||||
var root = Path.Combine(Path.GetTempPath(), "ikh-health-writable", Guid.NewGuid().ToString("N"));
|
||||
var configuration = new ConfigurationBuilder()
|
||||
.AddInMemoryCollection(new Dictionary<string, string?>
|
||||
{
|
||||
["KnowledgeHub:Paths:Data"] = Path.Combine(root, "data"),
|
||||
["KnowledgeHub:Paths:Attachments"] = Path.Combine(root, "attachments")
|
||||
})
|
||||
.Build();
|
||||
var check = new StorageHealthCheck(configuration);
|
||||
|
||||
try
|
||||
{
|
||||
var result = await check.CheckHealthAsync(new HealthCheckContext());
|
||||
|
||||
Assert.Equal(HealthStatus.Healthy, result.Status);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (Directory.Exists(root))
|
||||
{
|
||||
Directory.Delete(root, recursive: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task A_path_that_cannot_be_created_as_a_directory_is_unhealthy()
|
||||
{
|
||||
var blockingFile = Path.Combine(Path.GetTempPath(), $"ikh-health-blocking-{Guid.NewGuid():N}.tmp");
|
||||
await File.WriteAllBytesAsync(blockingFile, [1]);
|
||||
|
||||
try
|
||||
{
|
||||
// A configured path that collides with an existing *file* can never become
|
||||
// a writable directory — a real, not simulated, storage failure.
|
||||
var configuration = new ConfigurationBuilder()
|
||||
.AddInMemoryCollection(new Dictionary<string, string?> { ["KnowledgeHub:Paths:Data"] = blockingFile })
|
||||
.Build();
|
||||
var check = new StorageHealthCheck(configuration);
|
||||
|
||||
var result = await check.CheckHealthAsync(new HealthCheckContext());
|
||||
|
||||
Assert.Equal(HealthStatus.Unhealthy, result.Status);
|
||||
}
|
||||
finally
|
||||
{
|
||||
File.Delete(blockingFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user