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,108 @@
namespace StellaOps.Messaging.Testing.Builders;
/// <summary>
/// Builder for creating test messages with customizable properties.
/// </summary>
/// <typeparam name="TMessage">The message type.</typeparam>
public sealed class TestMessageBuilder<TMessage> where TMessage : class, new()
{
private readonly Dictionary<string, object?> _properties = new();
/// <summary>
/// Sets a property on the message.
/// </summary>
public TestMessageBuilder<TMessage> With(string propertyName, object? value)
{
_properties[propertyName] = value;
return this;
}
/// <summary>
/// Builds the message with all configured properties.
/// </summary>
public TMessage Build()
{
var message = new TMessage();
var type = typeof(TMessage);
foreach (var (propertyName, value) in _properties)
{
var property = type.GetProperty(propertyName);
if (property is not null && property.CanWrite)
{
property.SetValue(message, value);
}
}
return message;
}
}
/// <summary>
/// Simple test message for queue testing.
/// </summary>
public sealed record TestQueueMessage
{
/// <summary>
/// Gets or sets the message ID.
/// </summary>
public string Id { get; set; } = Guid.NewGuid().ToString("N");
/// <summary>
/// Gets or sets the message content.
/// </summary>
public string Content { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the priority.
/// </summary>
public int Priority { get; set; }
/// <summary>
/// Gets or sets the tenant ID.
/// </summary>
public string? TenantId { get; set; }
/// <summary>
/// Gets or sets when the message was created.
/// </summary>
public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.UtcNow;
/// <summary>
/// Gets or sets custom metadata.
/// </summary>
public Dictionary<string, string> Metadata { get; set; } = new();
}
/// <summary>
/// Extension methods for creating test messages.
/// </summary>
public static class TestMessageExtensions
{
/// <summary>
/// Creates a new test queue message with the specified content.
/// </summary>
public static TestQueueMessage CreateTestMessage(this string content, string? tenantId = null)
{
return new TestQueueMessage
{
Content = content,
TenantId = tenantId
};
}
/// <summary>
/// Creates multiple test messages for batch testing.
/// </summary>
public static IReadOnlyList<TestQueueMessage> CreateTestMessages(int count, string? tenantId = null)
{
return Enumerable.Range(1, count)
.Select(i => new TestQueueMessage
{
Content = $"Test message {i}",
TenantId = tenantId,
Priority = i % 3
})
.ToList();
}
}

View File

@@ -0,0 +1,84 @@
using Microsoft.Extensions.DependencyInjection;
using StellaOps.Messaging.Abstractions;
using StellaOps.Messaging.Transport.InMemory;
using Xunit;
namespace StellaOps.Messaging.Testing.Fixtures;
/// <summary>
/// xUnit fixture for in-memory messaging transport.
/// Provides fast, isolated test infrastructure without external dependencies.
/// </summary>
public sealed class InMemoryMessagingFixture : IAsyncLifetime
{
private InMemoryQueueRegistry _registry = null!;
/// <summary>
/// Gets the shared queue registry for test coordination.
/// </summary>
public InMemoryQueueRegistry Registry => _registry;
/// <inheritdoc />
public ValueTask InitializeAsync()
{
_registry = new InMemoryQueueRegistry();
return ValueTask.CompletedTask;
}
/// <inheritdoc />
public ValueTask DisposeAsync()
{
_registry.Clear();
return ValueTask.CompletedTask;
}
/// <summary>
/// Creates a service provider configured with in-memory transport.
/// </summary>
/// <returns>A configured service provider.</returns>
public IServiceProvider CreateServiceProvider()
{
var services = new ServiceCollection();
services.AddSingleton(_registry);
services.AddSingleton<IMessageQueueFactory, InMemoryMessageQueueFactory>();
services.AddSingleton<IDistributedCacheFactory, InMemoryCacheFactory>();
return services.BuildServiceProvider();
}
/// <summary>
/// Creates a message queue factory using in-memory transport.
/// </summary>
public IMessageQueueFactory CreateQueueFactory()
{
var sp = CreateServiceProvider();
return sp.GetRequiredService<IMessageQueueFactory>();
}
/// <summary>
/// Creates a cache factory using in-memory transport.
/// </summary>
public IDistributedCacheFactory CreateCacheFactory()
{
var sp = CreateServiceProvider();
return sp.GetRequiredService<IDistributedCacheFactory>();
}
/// <summary>
/// Clears all queues and caches in the registry.
/// Call this between tests for isolation.
/// </summary>
public void Reset()
{
_registry.Clear();
}
}
/// <summary>
/// Collection definition for in-memory messaging fixture sharing across test classes.
/// </summary>
[CollectionDefinition(nameof(InMemoryMessagingFixtureCollection))]
public class InMemoryMessagingFixtureCollection : ICollectionFixture<InMemoryMessagingFixture>
{
}

View File

@@ -0,0 +1,97 @@
using Microsoft.Extensions.DependencyInjection;
using StellaOps.Messaging.Abstractions;
using StellaOps.Messaging.Transport.Postgres;
using Testcontainers.PostgreSql;
using Xunit;
namespace StellaOps.Messaging.Testing.Fixtures;
/// <summary>
/// xUnit fixture for PostgreSQL testcontainer.
/// Provides a containerized PostgreSQL instance for queue integration tests.
/// </summary>
public sealed class PostgresQueueFixture : IAsyncLifetime
{
private readonly PostgreSqlContainer _container;
/// <summary>
/// Gets the connection string for the PostgreSQL instance.
/// </summary>
public string ConnectionString => _container.GetConnectionString();
/// <summary>
/// Initializes a new instance of the <see cref="PostgresQueueFixture"/> class.
/// </summary>
public PostgresQueueFixture()
{
_container = new PostgreSqlBuilder()
.WithImage("postgres:16-alpine")
.WithDatabase("stellaops_messaging_test")
.WithUsername("test")
.WithPassword("test")
.Build();
}
/// <inheritdoc />
public async ValueTask InitializeAsync()
{
await _container.StartAsync();
}
/// <inheritdoc />
public async ValueTask DisposeAsync()
{
await _container.StopAsync();
await _container.DisposeAsync();
}
/// <summary>
/// Creates a service provider configured with PostgreSQL transport.
/// </summary>
/// <param name="configureOptions">Optional configuration for PostgreSQL options.</param>
/// <returns>A configured service provider.</returns>
public IServiceProvider CreateServiceProvider(Action<PostgresTransportOptions>? configureOptions = null)
{
var services = new ServiceCollection();
services.AddOptions<PostgresTransportOptions>().Configure(options =>
{
options.ConnectionString = ConnectionString;
options.Schema = "messaging";
options.AutoCreateTables = true;
configureOptions?.Invoke(options);
});
services.AddSingleton<PostgresConnectionFactory>();
services.AddSingleton<IMessageQueueFactory, PostgresMessageQueueFactory>();
services.AddSingleton<IDistributedCacheFactory, PostgresCacheFactory>();
return services.BuildServiceProvider();
}
/// <summary>
/// Creates a message queue factory using PostgreSQL transport.
/// </summary>
public IMessageQueueFactory CreateQueueFactory()
{
var sp = CreateServiceProvider();
return sp.GetRequiredService<IMessageQueueFactory>();
}
/// <summary>
/// Creates a cache factory using PostgreSQL transport.
/// </summary>
public IDistributedCacheFactory CreateCacheFactory()
{
var sp = CreateServiceProvider();
return sp.GetRequiredService<IDistributedCacheFactory>();
}
}
/// <summary>
/// Collection definition for PostgreSQL fixture sharing across test classes.
/// </summary>
[CollectionDefinition(nameof(PostgresQueueFixtureCollection))]
public class PostgresQueueFixtureCollection : ICollectionFixture<PostgresQueueFixture>
{
}

View File

@@ -0,0 +1,108 @@
using DotNet.Testcontainers.Builders;
using DotNet.Testcontainers.Containers;
using Microsoft.Extensions.DependencyInjection;
using StellaOps.Messaging.Abstractions;
using StellaOps.Messaging.Transport.Valkey;
using Xunit;
namespace StellaOps.Messaging.Testing.Fixtures;
/// <summary>
/// xUnit fixture for Valkey testcontainer.
/// Provides a containerized Valkey instance for integration tests.
/// </summary>
public sealed class ValkeyFixture : IAsyncLifetime
{
private readonly IContainer _container;
/// <summary>
/// Gets the connection string for the Valkey instance.
/// </summary>
public string ConnectionString { get; private set; } = null!;
/// <summary>
/// Gets the host of the Valkey instance.
/// </summary>
public string Host => _container.Hostname;
/// <summary>
/// Gets the mapped port for the Valkey instance.
/// </summary>
public ushort Port { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="ValkeyFixture"/> class.
/// </summary>
public ValkeyFixture()
{
_container = new ContainerBuilder()
.WithImage("valkey/valkey:8-alpine")
.WithPortBinding(6379, true)
.WithWaitStrategy(Wait.ForUnixContainer()
.UntilCommandIsCompleted("valkey-cli", "ping"))
.Build();
}
/// <inheritdoc />
public async ValueTask InitializeAsync()
{
await _container.StartAsync();
Port = _container.GetMappedPublicPort(6379);
ConnectionString = $"{Host}:{Port}";
}
/// <inheritdoc />
public async ValueTask DisposeAsync()
{
await _container.StopAsync();
await _container.DisposeAsync();
}
/// <summary>
/// Creates a service provider configured with Valkey transport.
/// </summary>
/// <param name="configureOptions">Optional configuration for Valkey options.</param>
/// <returns>A configured service provider.</returns>
public IServiceProvider CreateServiceProvider(Action<ValkeyTransportOptions>? configureOptions = null)
{
var services = new ServiceCollection();
services.AddOptions<ValkeyTransportOptions>().Configure(options =>
{
options.ConnectionString = ConnectionString;
configureOptions?.Invoke(options);
});
services.AddSingleton<ValkeyConnectionFactory>();
services.AddSingleton<IMessageQueueFactory, ValkeyMessageQueueFactory>();
services.AddSingleton<IDistributedCacheFactory, ValkeyCacheFactory>();
return services.BuildServiceProvider();
}
/// <summary>
/// Creates a message queue factory using Valkey transport.
/// </summary>
public IMessageQueueFactory CreateQueueFactory()
{
var sp = CreateServiceProvider();
return sp.GetRequiredService<IMessageQueueFactory>();
}
/// <summary>
/// Creates a cache factory using Valkey transport.
/// </summary>
public IDistributedCacheFactory CreateCacheFactory()
{
var sp = CreateServiceProvider();
return sp.GetRequiredService<IDistributedCacheFactory>();
}
}
/// <summary>
/// Collection definition for Valkey fixture sharing across test classes.
/// </summary>
[CollectionDefinition(nameof(ValkeyFixtureCollection))]
public class ValkeyFixtureCollection : ICollectionFixture<ValkeyFixture>
{
}

View File

@@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<UseXunitV3>true</UseXunitV3>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>preview</LangVersion>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<RootNamespace>StellaOps.Messaging.Testing</RootNamespace>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\__Libraries\StellaOps.Messaging\StellaOps.Messaging.csproj" />
<ProjectReference Include="..\..\..\__Libraries\StellaOps.Messaging.Transport.Valkey\StellaOps.Messaging.Transport.Valkey.csproj" />
<ProjectReference Include="..\..\..\__Libraries\StellaOps.Messaging.Transport.Postgres\StellaOps.Messaging.Transport.Postgres.csproj" />
<ProjectReference Include="..\..\..\__Libraries\StellaOps.Messaging.Transport.InMemory\StellaOps.Messaging.Transport.InMemory.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
<PackageReference Include="Testcontainers" />
<PackageReference Include="Testcontainers.PostgreSql" />
<PackageReference Include="xunit.v3.extensibility.core" PrivateAssets="all" />
</ItemGroup>
</Project>