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,127 @@
using System.Reflection;
using Npgsql;
using StellaOps.AirGap.Persistence.Postgres;
using StellaOps.Infrastructure.Postgres.Testing;
using Xunit;
namespace StellaOps.AirGap.Persistence.Tests;
/// <summary>
/// PostgreSQL integration test fixture for the AirGap module.
/// Runs migrations from embedded resources and provides test isolation.
/// </summary>
public sealed class AirGapPostgresFixture : PostgresIntegrationFixture, ICollectionFixture<AirGapPostgresFixture>
{
protected override Assembly? GetMigrationAssembly()
=> typeof(AirGapDataSource).Assembly;
protected override string GetModuleName() => "AirGap";
protected override string? GetResourcePrefix() => "Migrations";
/// <summary>
/// Gets all table names in the test schema.
/// </summary>
public async Task<IReadOnlyList<string>> GetTableNamesAsync(CancellationToken cancellationToken = default)
{
await using var connection = new NpgsqlConnection(ConnectionString);
await connection.OpenAsync(cancellationToken).ConfigureAwait(false);
await using var cmd = new NpgsqlCommand(
"""
SELECT table_name FROM information_schema.tables
WHERE table_schema = @schema AND table_type = 'BASE TABLE';
""",
connection);
cmd.Parameters.AddWithValue("schema", SchemaName);
var tables = new List<string>();
await using var reader = await cmd.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false);
while (await reader.ReadAsync(cancellationToken).ConfigureAwait(false))
{
tables.Add(reader.GetString(0));
}
return tables;
}
/// <summary>
/// Gets all column names for a specific table in the test schema.
/// </summary>
public async Task<IReadOnlyList<string>> GetColumnNamesAsync(string tableName, CancellationToken cancellationToken = default)
{
await using var connection = new NpgsqlConnection(ConnectionString);
await connection.OpenAsync(cancellationToken).ConfigureAwait(false);
await using var cmd = new NpgsqlCommand(
"""
SELECT column_name FROM information_schema.columns
WHERE table_schema = @schema AND table_name = @table;
""",
connection);
cmd.Parameters.AddWithValue("schema", SchemaName);
cmd.Parameters.AddWithValue("table", tableName);
var columns = new List<string>();
await using var reader = await cmd.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false);
while (await reader.ReadAsync(cancellationToken).ConfigureAwait(false))
{
columns.Add(reader.GetString(0));
}
return columns;
}
/// <summary>
/// Gets all index names for a specific table in the test schema.
/// </summary>
public async Task<IReadOnlyList<string>> GetIndexNamesAsync(string tableName, CancellationToken cancellationToken = default)
{
await using var connection = new NpgsqlConnection(ConnectionString);
await connection.OpenAsync(cancellationToken).ConfigureAwait(false);
await using var cmd = new NpgsqlCommand(
"""
SELECT indexname FROM pg_indexes
WHERE schemaname = @schema AND tablename = @table;
""",
connection);
cmd.Parameters.AddWithValue("schema", SchemaName);
cmd.Parameters.AddWithValue("table", tableName);
var indexes = new List<string>();
await using var reader = await cmd.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false);
while (await reader.ReadAsync(cancellationToken).ConfigureAwait(false))
{
indexes.Add(reader.GetString(0));
}
return indexes;
}
/// <summary>
/// Ensures migrations have been run. This is idempotent and safe to call multiple times.
/// </summary>
public async Task EnsureMigrationsRunAsync(CancellationToken cancellationToken = default)
{
var migrationAssembly = GetMigrationAssembly();
if (migrationAssembly != null)
{
await Fixture.RunMigrationsFromAssemblyAsync(
migrationAssembly,
GetModuleName(),
GetResourcePrefix(),
cancellationToken).ConfigureAwait(false);
}
}
}
/// <summary>
/// Collection definition for AirGap PostgreSQL integration tests.
/// Tests in this collection share a single PostgreSQL container instance.
/// </summary>
[CollectionDefinition(Name)]
public sealed class AirGapPostgresCollection : ICollectionFixture<AirGapPostgresFixture>
{
public const string Name = "AirGapPostgres";
}