Fix build and code structure improvements. New but essential UI functionality. CI improvements. Documentation improvements. AI module improvements.

This commit is contained in:
StellaOps Bot
2025-12-26 21:54:17 +02:00
parent 335ff7da16
commit c2b9cd8d1f
3717 changed files with 264714 additions and 48202 deletions

View File

@@ -0,0 +1,153 @@
using System.Reflection;
using Xunit;
using Xunit.Sdk;
namespace StellaOps.Infrastructure.Postgres.Testing;
/// <summary>
/// Marks a test method as a migration test that requires database isolation.
/// When applied, the test will automatically truncate all tables before execution.
/// </summary>
/// <remarks>
/// Use this attribute on test methods that modify database state and need isolation
/// from other tests. The attribute ensures a clean database state by truncating
/// all tables in the test schema before the test runs.
///
/// Example:
/// <code>
/// [MigrationTest]
/// public async Task Should_Insert_Record_Successfully()
/// {
/// // Database tables are empty when this test starts
/// await _fixture.ExecuteSqlAsync("INSERT INTO ...");
/// }
/// </code>
/// </remarks>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public sealed class MigrationTestAttribute : BeforeAfterTestAttribute
{
/// <summary>
/// Gets or sets whether to truncate tables before the test runs.
/// Default is true.
/// </summary>
public bool TruncateBefore { get; set; } = true;
/// <summary>
/// Gets or sets whether to truncate tables after the test runs.
/// Default is false (let the next test with TruncateBefore handle cleanup).
/// </summary>
public bool TruncateAfter { get; set; }
/// <summary>
/// Gets or sets specific table names to truncate. If null or empty, all tables are truncated.
/// </summary>
public string[]? Tables { get; set; }
/// <summary>
/// Called before the test method runs.
/// </summary>
public override void Before(MethodInfo methodUnderTest)
{
if (!TruncateBefore)
{
return;
}
// Try to find the fixture from the test class
var testClass = methodUnderTest.DeclaringType;
if (testClass is null) return;
// Look for a field or property of type PostgresIntegrationFixture
var fixtureField = testClass
.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)
.FirstOrDefault(f => typeof(PostgresIntegrationFixture).IsAssignableFrom(f.FieldType));
var fixtureProperty = testClass
.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)
.FirstOrDefault(p => typeof(PostgresIntegrationFixture).IsAssignableFrom(p.PropertyType));
// Note: We can't access the instance here in xUnit's BeforeAfterTestAttribute
// This is a limitation - the actual truncation needs to be done via a different mechanism
// See MigrationTestFixture for a better approach
}
/// <summary>
/// Called after the test method runs.
/// </summary>
public override void After(MethodInfo methodUnderTest)
{
// Cleanup is optional and typically not needed
}
}
/// <summary>
/// A test fixture wrapper that provides automatic table truncation between tests.
/// </summary>
/// <typeparam name="TFixture">The underlying PostgreSQL integration fixture type.</typeparam>
public abstract class MigrationTestBase<TFixture> : IAsyncLifetime
where TFixture : PostgresIntegrationFixture
{
private readonly TFixture _fixture;
/// <summary>
/// Gets the underlying test fixture.
/// </summary>
protected TFixture Fixture => _fixture;
/// <summary>
/// Gets the connection string for direct database access.
/// </summary>
protected string ConnectionString => _fixture.ConnectionString;
/// <summary>
/// Gets the schema name for this test.
/// </summary>
protected string SchemaName => _fixture.SchemaName;
/// <summary>
/// Creates a new migration test base with the specified fixture.
/// </summary>
protected MigrationTestBase(TFixture fixture)
{
_fixture = fixture ?? throw new ArgumentNullException(nameof(fixture));
}
/// <summary>
/// Called before each test. Override to customize initialization.
/// By default, truncates all tables for test isolation.
/// </summary>
public virtual async Task InitializeAsync()
{
await _fixture.TruncateAllTablesAsync().ConfigureAwait(false);
}
/// <summary>
/// Called after each test. Override to customize cleanup.
/// </summary>
public virtual Task DisposeAsync()
{
return Task.CompletedTask;
}
/// <summary>
/// Executes raw SQL for test setup.
/// </summary>
protected Task ExecuteSqlAsync(string sql, CancellationToken cancellationToken = default)
=> _fixture.ExecuteSqlAsync(sql, cancellationToken);
}
/// <summary>
/// Collection definition for migration tests that require database isolation.
/// </summary>
/// <remarks>
/// Apply [Collection(MigrationTestCollection.Name)] to test classes that share a database fixture.
/// Tests within the collection run sequentially to avoid database conflicts.
/// </remarks>
public static class MigrationTestCollection
{
/// <summary>
/// The collection name for migration tests.
/// </summary>
public const string Name = "MigrationTests";
}

View File

@@ -14,8 +14,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Testcontainers.PostgreSql" Version="4.1.0" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="Testcontainers.PostgreSql" />
<PackageReference Include="xunit" PrivateAssets="all" />
</ItemGroup>
<ItemGroup>