stabilize tests

This commit is contained in:
master
2026-02-01 21:37:40 +02:00
parent 55744f6a39
commit 5d5e80b2e4
6435 changed files with 33984 additions and 13802 deletions

View File

@@ -0,0 +1,8 @@
# StellaOps.Chaos.ControlPlane.Tests Task Board
This board mirrors active sprint tasks for this module.
Source of truth: `docs/implplan/SPRINT_20260130_002_Tools_csproj_remediation_solid_review.md`.
| Task ID | Status | Notes |
| --- | --- | --- |
| REMED-05 | TODO | Remediation checklist: docs/implplan/audits/csproj-standards/remediation/checklists/src/__Tests/chaos/StellaOps.Chaos.ControlPlane.Tests/StellaOps.Chaos.ControlPlane.Tests.md. |
| REMED-06 | DONE | SOLID review notes captured for SPRINT_20260130_002. |

View File

@@ -148,10 +148,13 @@ public class BackpressureVerificationTests : IClassFixture<RouterTestFixture>
{
var metrics = await metricsResponse.Content.ReadAsStringAsync();
// Basic metric checks (actual metric names depend on implementation)
// These are common Prometheus-style metric names
// Check for Gateway-specific or ASP.NET Core metrics
var expectedMetrics = new[]
{
"gateway_active_connections",
"gateway_registered_endpoints",
"http_server_request_duration",
"http_server_active_requests",
"http_requests_total",
"http_request_duration",
};

View File

@@ -0,0 +1,162 @@
// -----------------------------------------------------------------------------
// ChaosGatewayFactory.cs
// Sprint: SPRINT_20260201_002_QA_chaos_parity_enablement
// Description: In-process WebApplicationFactory for Gateway, used by chaos tests
// when no external ROUTER_URL is configured. Registers a stub
// microservice so /api/v1/scan routes through the full middleware
// pipeline (including rate limiting) and returns 202 Accepted.
// -----------------------------------------------------------------------------
using System.Text;
using System.Text.Json;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using StellaOps.Router.Common.Abstractions;
using StellaOps.Router.Common.Enums;
using StellaOps.Router.Common.Frames;
using StellaOps.Router.Common.Models;
using StellaOps.Router.Gateway.Configuration;
namespace StellaOps.Chaos.Router.Tests.Fixtures;
/// <summary>
/// Hosts the Gateway WebService in-process for chaos testing.
/// Registers a stub microservice connection and transport so that requests to
/// <c>/api/v1/scan</c> flow through the complete middleware pipeline
/// (including rate limiting) and return <c>202 Accepted</c>.
/// </summary>
public sealed class ChaosGatewayFactory : WebApplicationFactory<Program>
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.UseEnvironment("Development");
builder.ConfigureTestServices(services =>
{
services.Configure<RouterNodeConfig>(config =>
{
config.Region = "test";
config.NodeId = "chaos-test-01";
config.Environment = "test";
});
// Replace transport client with a stub that returns 202
services.RemoveAll<ITransportClient>();
services.AddSingleton<ITransportClient, StubTransportClient>();
// Register a hosted service that seeds the routing state with a stub endpoint
services.AddHostedService<StubMicroserviceRegistrar>();
});
}
}
/// <summary>
/// A stub transport client that returns <c>202 Accepted</c> for any request.
/// Used by chaos tests to exercise the full Gateway middleware pipeline without
/// needing a real microservice connected via TCP/TLS/InMemory transport.
/// </summary>
internal sealed class StubTransportClient : ITransportClient
{
private static readonly JsonSerializerOptions JsonOptions = new()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
public Task<Frame> SendRequestAsync(
ConnectionState connection,
Frame requestFrame,
TimeSpan timeout,
CancellationToken cancellationToken)
{
// Parse the request to get the request ID
var request = FrameConverter.ToRequestFrame(requestFrame);
var requestId = request?.RequestId ?? Guid.NewGuid().ToString("N");
// Build a 202 Accepted response
var responseFrame = new ResponseFrame
{
RequestId = requestId,
StatusCode = 202,
Headers = new Dictionary<string, string>
{
["Content-Type"] = "application/json; charset=utf-8"
},
Payload = Encoding.UTF8.GetBytes(JsonSerializer.Serialize(
new { status = "accepted", scanId = requestId },
JsonOptions))
};
return Task.FromResult(FrameConverter.ToFrame(responseFrame));
}
public Task SendCancelAsync(
ConnectionState connection,
Guid correlationId,
string? reason = null)
{
return Task.CompletedTask;
}
public Task SendStreamingAsync(
ConnectionState connection,
Frame requestHeader,
Stream requestBody,
Func<Stream, Task> readResponseBody,
PayloadLimits limits,
CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
}
/// <summary>
/// Registers a stub microservice connection in <see cref="IGlobalRoutingState"/>
/// at startup so that <c>POST /api/v1/scan</c> resolves through the endpoint
/// resolution middleware instead of returning 404.
/// </summary>
internal sealed class StubMicroserviceRegistrar : IHostedService
{
private readonly IGlobalRoutingState _routingState;
public StubMicroserviceRegistrar(IGlobalRoutingState routingState)
{
_routingState = routingState;
}
public Task StartAsync(CancellationToken cancellationToken)
{
var scanEndpoint = new EndpointDescriptor
{
ServiceName = "chaos-stub-scanner",
Version = "1.0.0",
Method = "POST",
Path = "/api/v1/scan"
};
var connection = new ConnectionState
{
ConnectionId = "chaos-stub-001",
Instance = new InstanceDescriptor
{
InstanceId = "chaos-stub-instance-001",
ServiceName = "chaos-stub-scanner",
Version = "1.0.0",
Region = "test"
},
Status = InstanceHealthStatus.Healthy,
TransportType = TransportType.InMemory
};
connection.Endpoints.Add(("POST", "/api/v1/scan"), scanEndpoint);
_routingState.AddConnection(connection);
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}

View File

@@ -1,8 +1,10 @@
// -----------------------------------------------------------------------------
// RouterTestFixture.cs
// Sprint: SPRINT_5100_0005_0001_router_chaos_suite
// Task: T2 - Backpressure Verification Tests
// Sprint: SPRINT_20260201_002_QA_chaos_parity_enablement
// Task: Enable in-process Gateway hosting for chaos tests
// Description: Test fixture for router chaos testing with Valkey support.
// When ROUTER_URL is set, connects to external router.
// Otherwise, hosts Gateway in-process via ChaosGatewayFactory.
// -----------------------------------------------------------------------------
using System.Net.Http.Json;
@@ -14,25 +16,27 @@ namespace StellaOps.Chaos.Router.Tests.Fixtures;
/// Test fixture providing an HTTP client for router chaos testing.
/// </summary>
/// <remarks>
/// <para>
/// When <c>ROUTER_URL</c> is set, the fixture connects to the external router.
/// When it is not set, the fixture hosts the Gateway in-process using
/// <see cref="ChaosGatewayFactory"/> so tests run without external infrastructure.
/// </para>
/// <para>
/// In xUnit v3, throwing SkipException from fixture InitializeAsync causes test failures
/// rather than skips. Instead, we track availability and let tests call EnsureRouterAvailable()
/// to skip when infrastructure is unavailable.
/// </para>
/// </remarks>
public class RouterTestFixture : IAsyncLifetime
{
private readonly HttpClient _client;
private readonly string _routerUrl;
private HttpClient? _client;
private readonly string? _routerUrl;
private string? _skipReason;
private ChaosGatewayFactory? _factory;
public RouterTestFixture()
{
_routerUrl = Environment.GetEnvironmentVariable("ROUTER_URL") ?? "http://localhost:8080";
_client = new HttpClient
{
BaseAddress = new Uri(_routerUrl),
Timeout = TimeSpan.FromSeconds(30)
};
_routerUrl = Environment.GetEnvironmentVariable("ROUTER_URL");
}
/// <summary>
@@ -56,10 +60,10 @@ public class RouterTestFixture : IAsyncLifetime
public HttpClient CreateClient()
{
EnsureRouterAvailable();
return _client;
return _client!;
}
public string RouterUrl => _routerUrl;
public string RouterUrl => _routerUrl ?? _factory?.Server.BaseAddress.ToString() ?? "http://localhost:8080";
/// <summary>
/// Configure router with lower limits for overload testing.
@@ -89,22 +93,51 @@ public class RouterTestFixture : IAsyncLifetime
public async ValueTask InitializeAsync()
{
try
if (_routerUrl is not null)
{
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(2));
_ = await _client.GetAsync("/", cts.Token);
_skipReason = null;
// External router mode: connect to the specified URL
_client = new HttpClient
{
BaseAddress = new Uri(_routerUrl),
Timeout = TimeSpan.FromSeconds(30)
};
try
{
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(2));
_ = await _client.GetAsync("/", cts.Token);
_skipReason = null;
}
catch (Exception ex) when (ex is HttpRequestException or TaskCanceledException or OperationCanceledException)
{
_skipReason = $"Router not reachable at '{_routerUrl}'. Set ROUTER_URL or start the router service to run chaos tests.";
}
}
catch (Exception ex) when (ex is HttpRequestException || ex is TaskCanceledException || ex is OperationCanceledException)
else
{
_skipReason = $"Router not reachable at '{_routerUrl}'. Set ROUTER_URL or start the router service to run chaos tests.";
// In-process mode: host Gateway via WebApplicationFactory
try
{
_factory = new ChaosGatewayFactory();
_client = _factory.CreateClient();
_client.Timeout = TimeSpan.FromSeconds(30);
_skipReason = null;
}
catch (Exception ex)
{
_skipReason = $"Failed to start in-process Gateway: {ex.Message}";
}
}
}
public ValueTask DisposeAsync()
public async ValueTask DisposeAsync()
{
_client.Dispose();
return ValueTask.CompletedTask;
_client?.Dispose();
if (_factory is not null)
{
await _factory.DisposeAsync();
}
}
}
@@ -165,6 +198,3 @@ public class RouterWithValkeyFixture : RouterTestFixture
await base.DisposeAsync();
}
}

View File

@@ -10,7 +10,12 @@
<ItemGroup>
<PackageReference Include="FluentAssertions" />
<PackageReference Include="Testcontainers" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" />
<PackageReference Include="Testcontainers" />
<PackageReference Include="Testcontainers.Redis" />
</ItemGroup>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Router\StellaOps.Gateway.WebService\StellaOps.Gateway.WebService.csproj" />
</ItemGroup>
</Project>

View File

@@ -8,3 +8,4 @@ Source of truth: `docs-archived/implplan/2025-12-29-csproj-audit/SPRINT_20251229
| AUDIT-0136-M | DONE | Maintainability audit for StellaOps.Chaos.Router.Tests. |
| AUDIT-0136-T | DONE | Test coverage audit for StellaOps.Chaos.Router.Tests. |
| AUDIT-0136-A | TODO | Pending approval for changes. |
| REMED-06 | DONE | SOLID review notes captured for SPRINT_20260130_002. |