stabilize tests
This commit is contained in:
@@ -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. |
|
||||
@@ -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",
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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>
|
||||
@@ -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. |
|
||||
|
||||
Reference in New Issue
Block a user