consolidate the tests locations

This commit is contained in:
StellaOps Bot
2025-12-26 01:48:24 +02:00
parent 17613acf57
commit 39359da171
2031 changed files with 2607 additions and 476 deletions

View File

@@ -1,210 +0,0 @@
using StellaOps.Router.Common.Enums;
using StellaOps.Router.Common.Frames;
using StellaOps.Router.Common.Models;
namespace StellaOps.Router.Testing.Factories;
/// <summary>
/// Factory for creating test frames with sensible defaults.
/// </summary>
public static class TestFrameFactory
{
/// <summary>
/// Creates a request frame with the specified payload.
/// </summary>
public static Frame CreateRequestFrame(
byte[]? payload = null,
string? correlationId = null,
FrameType frameType = FrameType.Request)
{
return new Frame
{
Type = frameType,
CorrelationId = correlationId ?? Guid.NewGuid().ToString("N"),
Payload = payload ?? Array.Empty<byte>()
};
}
/// <summary>
/// Creates a response frame for the given correlation ID.
/// </summary>
public static Frame CreateResponseFrame(
string correlationId,
byte[]? payload = null)
{
return new Frame
{
Type = FrameType.Response,
CorrelationId = correlationId,
Payload = payload ?? Array.Empty<byte>()
};
}
/// <summary>
/// Creates a hello frame for service registration.
/// </summary>
public static Frame CreateHelloFrame(
string serviceName = "test-service",
string version = "1.0.0",
string region = "test",
string instanceId = "test-instance",
IReadOnlyList<EndpointDescriptor>? endpoints = null)
{
var helloPayload = new HelloPayload
{
Instance = new InstanceDescriptor
{
InstanceId = instanceId,
ServiceName = serviceName,
Version = version,
Region = region
},
Endpoints = endpoints ?? []
};
return new Frame
{
Type = FrameType.Hello,
CorrelationId = Guid.NewGuid().ToString("N"),
Payload = System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(helloPayload)
};
}
/// <summary>
/// Creates a heartbeat frame.
/// </summary>
public static Frame CreateHeartbeatFrame(
string instanceId = "test-instance",
InstanceHealthStatus status = InstanceHealthStatus.Healthy,
int inFlightRequestCount = 0,
double errorRate = 0.0)
{
var heartbeatPayload = new HeartbeatPayload
{
InstanceId = instanceId,
Status = status,
InFlightRequestCount = inFlightRequestCount,
ErrorRate = errorRate,
TimestampUtc = DateTime.UtcNow
};
return new Frame
{
Type = FrameType.Heartbeat,
CorrelationId = Guid.NewGuid().ToString("N"),
Payload = System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(heartbeatPayload)
};
}
/// <summary>
/// Creates a cancel frame for the given correlation ID.
/// </summary>
public static Frame CreateCancelFrame(
string correlationId,
string? reason = null)
{
var cancelPayload = new CancelPayload
{
Reason = reason ?? CancelReasons.Timeout
};
return new Frame
{
Type = FrameType.Cancel,
CorrelationId = correlationId,
Payload = System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(cancelPayload)
};
}
/// <summary>
/// Creates a frame with a specific payload size for testing limits.
/// </summary>
public static Frame CreateFrameWithPayloadSize(int payloadSize)
{
var payload = new byte[payloadSize];
Random.Shared.NextBytes(payload);
return new Frame
{
Type = FrameType.Request,
CorrelationId = Guid.NewGuid().ToString("N"),
Payload = payload
};
}
/// <summary>
/// Creates a request frame from JSON content.
/// </summary>
public static RequestFrame CreateTypedRequestFrame<T>(
T request,
string method = "POST",
string path = "/test",
Dictionary<string, string>? headers = null)
{
return new RequestFrame
{
RequestId = Guid.NewGuid().ToString("N"),
CorrelationId = Guid.NewGuid().ToString("N"),
Method = method,
Path = path,
Headers = headers ?? new Dictionary<string, string>(),
Payload = System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(request)
};
}
/// <summary>
/// Creates an endpoint descriptor for testing.
/// </summary>
public static EndpointDescriptor CreateEndpointDescriptor(
string method = "GET",
string path = "/test",
string serviceName = "test-service",
string version = "1.0.0",
int timeoutSeconds = 30,
bool supportsStreaming = false,
IReadOnlyList<ClaimRequirement>? requiringClaims = null)
{
return new EndpointDescriptor
{
Method = method,
Path = path,
ServiceName = serviceName,
Version = version,
DefaultTimeout = TimeSpan.FromSeconds(timeoutSeconds),
SupportsStreaming = supportsStreaming,
RequiringClaims = requiringClaims ?? []
};
}
/// <summary>
/// Creates an instance descriptor for testing.
/// </summary>
public static InstanceDescriptor CreateInstanceDescriptor(
string instanceId = "test-instance",
string serviceName = "test-service",
string version = "1.0.0",
string region = "test")
{
return new InstanceDescriptor
{
InstanceId = instanceId,
ServiceName = serviceName,
Version = version,
Region = region
};
}
/// <summary>
/// Creates a claim requirement for testing.
/// </summary>
public static ClaimRequirement CreateClaimRequirement(
string type,
string? value = null)
{
return new ClaimRequirement
{
Type = type,
Value = value
};
}
}

View File

@@ -1,102 +0,0 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Xunit;
namespace StellaOps.Router.Testing.Fixtures;
/// <summary>
/// Base test fixture for Router tests providing common utilities.
/// Implements IAsyncLifetime for async setup/teardown.
/// </summary>
public abstract class RouterTestFixture : IAsyncLifetime
{
/// <summary>
/// Gets a null logger factory for tests that don't need logging.
/// </summary>
protected ILoggerFactory LoggerFactory { get; } = NullLoggerFactory.Instance;
/// <summary>
/// Gets a null logger for tests that don't need logging.
/// </summary>
protected ILogger<T> GetLogger<T>() => NullLogger<T>.Instance;
/// <summary>
/// Creates a cancellation token that times out after the specified duration.
/// </summary>
protected static CancellationToken CreateTimeoutToken(TimeSpan timeout)
{
var cts = new CancellationTokenSource(timeout);
return cts.Token;
}
/// <summary>
/// Creates a cancellation token that times out after 5 seconds (default for tests).
/// </summary>
protected static CancellationToken CreateTestTimeoutToken()
{
return CreateTimeoutToken(TimeSpan.FromSeconds(5));
}
/// <summary>
/// Waits for a condition to be true with timeout.
/// </summary>
protected static async Task WaitForConditionAsync(
Func<bool> condition,
TimeSpan timeout,
TimeSpan? pollInterval = null)
{
var interval = pollInterval ?? TimeSpan.FromMilliseconds(50);
var deadline = DateTimeOffset.UtcNow + timeout;
while (DateTimeOffset.UtcNow < deadline)
{
if (condition())
return;
await Task.Delay(interval);
}
throw new TimeoutException($"Condition not met within {timeout}");
}
/// <summary>
/// Waits for an async condition to be true with timeout.
/// </summary>
protected static async Task WaitForConditionAsync(
Func<Task<bool>> condition,
TimeSpan timeout,
TimeSpan? pollInterval = null)
{
var interval = pollInterval ?? TimeSpan.FromMilliseconds(50);
var deadline = DateTimeOffset.UtcNow + timeout;
while (DateTimeOffset.UtcNow < deadline)
{
if (await condition())
return;
await Task.Delay(interval);
}
throw new TimeoutException($"Condition not met within {timeout}");
}
/// <summary>
/// Override for async initialization.
/// </summary>
public virtual Task InitializeAsync() => Task.CompletedTask;
/// <summary>
/// Override for async cleanup.
/// </summary>
public virtual Task DisposeAsync() => Task.CompletedTask;
}
/// <summary>
/// Collection fixture for sharing state across tests in the same collection.
/// </summary>
public abstract class RouterCollectionFixture : IAsyncLifetime
{
public virtual Task InitializeAsync() => Task.CompletedTask;
public virtual Task DisposeAsync() => Task.CompletedTask;
}

View File

@@ -1,56 +0,0 @@
using StellaOps.Router.Common.Enums;
using StellaOps.Router.Common.Models;
namespace StellaOps.Router.Testing.Mocks;
/// <summary>
/// A mock connection state for testing routing and connection management.
/// </summary>
public sealed class MockConnectionState
{
public string ConnectionId { get; init; } = Guid.NewGuid().ToString("N");
public string ServiceName { get; init; } = "test-service";
public string Version { get; init; } = "1.0.0";
public string Region { get; init; } = "test";
public string InstanceId { get; init; } = "test-instance";
public InstanceHealthStatus HealthStatus { get; set; } = InstanceHealthStatus.Healthy;
public DateTimeOffset ConnectedAtUtc { get; init; } = DateTimeOffset.UtcNow;
public DateTimeOffset LastHeartbeatUtc { get; set; } = DateTimeOffset.UtcNow;
public int InflightRequests { get; set; }
public int Weight { get; set; } = 100;
public List<EndpointDescriptor> Endpoints { get; init; } = new();
/// <summary>
/// Creates a connection state for testing.
/// </summary>
public static MockConnectionState Create(
string? serviceName = null,
string? instanceId = null,
InstanceHealthStatus status = InstanceHealthStatus.Healthy)
{
return new MockConnectionState
{
ServiceName = serviceName ?? "test-service",
InstanceId = instanceId ?? $"instance-{Guid.NewGuid():N}",
HealthStatus = status
};
}
/// <summary>
/// Creates multiple connection states simulating a service cluster.
/// </summary>
public static List<MockConnectionState> CreateCluster(
string serviceName,
int instanceCount,
InstanceHealthStatus status = InstanceHealthStatus.Healthy)
{
return Enumerable.Range(0, instanceCount)
.Select(i => new MockConnectionState
{
ServiceName = serviceName,
InstanceId = $"{serviceName}-{i}",
HealthStatus = status
})
.ToList();
}
}

View File

@@ -1,104 +0,0 @@
using Microsoft.Extensions.Logging;
using System.Collections.Concurrent;
namespace StellaOps.Router.Testing.Mocks;
/// <summary>
/// A logger that records all log entries for assertions.
/// </summary>
public sealed class RecordingLogger<T> : ILogger<T>
{
private readonly ConcurrentQueue<LogEntry> _entries = new();
/// <summary>
/// Gets all recorded log entries.
/// </summary>
public IReadOnlyList<LogEntry> Entries => _entries.ToList();
/// <summary>
/// Gets entries filtered by log level.
/// </summary>
public IEnumerable<LogEntry> GetEntries(LogLevel level) =>
_entries.Where(e => e.Level == level);
/// <summary>
/// Gets all error entries.
/// </summary>
public IEnumerable<LogEntry> Errors => GetEntries(LogLevel.Error);
/// <summary>
/// Gets all warning entries.
/// </summary>
public IEnumerable<LogEntry> Warnings => GetEntries(LogLevel.Warning);
/// <summary>
/// Clears all recorded entries.
/// </summary>
public void Clear()
{
while (_entries.TryDequeue(out _)) { }
}
public IDisposable? BeginScope<TState>(TState state) where TState : notnull =>
NullScope.Instance;
public bool IsEnabled(LogLevel logLevel) => true;
public void Log<TState>(
LogLevel logLevel,
EventId eventId,
TState state,
Exception? exception,
Func<TState, Exception?, string> formatter)
{
_entries.Enqueue(new LogEntry
{
Level = logLevel,
EventId = eventId,
Message = formatter(state, exception),
Exception = exception,
Timestamp = DateTimeOffset.UtcNow
});
}
private sealed class NullScope : IDisposable
{
public static readonly NullScope Instance = new();
public void Dispose() { }
}
}
/// <summary>
/// Represents a recorded log entry.
/// </summary>
public sealed record LogEntry
{
public required LogLevel Level { get; init; }
public required EventId EventId { get; init; }
public required string Message { get; init; }
public Exception? Exception { get; init; }
public DateTimeOffset Timestamp { get; init; }
}
/// <summary>
/// A logger factory that creates recording loggers.
/// </summary>
public sealed class RecordingLoggerFactory : ILoggerFactory
{
private readonly ConcurrentDictionary<string, object> _loggers = new();
public ILogger CreateLogger(string categoryName) =>
(ILogger)_loggers.GetOrAdd(categoryName, _ => new RecordingLogger<object>());
public ILogger<T> CreateLogger<T>() =>
(ILogger<T>)_loggers.GetOrAdd(typeof(T).FullName!, _ => new RecordingLogger<T>());
public RecordingLogger<T>? GetLogger<T>() =>
_loggers.TryGetValue(typeof(T).FullName!, out var logger)
? logger as RecordingLogger<T>
: null;
public void AddProvider(ILoggerProvider provider) { }
public void Dispose() { }
}

View File

@@ -1,24 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<LangVersion>preview</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<IsPackable>false</IsPackable>
<RootNamespace>StellaOps.Router.Testing</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.0" />
<PackageReference Include="Microsoft.Extensions.Options" Version="10.0.0" />
<PackageReference Include="xunit" Version="2.9.2" />
<PackageReference Include="FluentAssertions" Version="6.12.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\StellaOps.Router.Common\StellaOps.Router.Common.csproj" />
</ItemGroup>
</Project>