Add unit tests and logging infrastructure for InMemory and RabbitMQ transports
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled

- Implemented RecordingLogger and RecordingLoggerFactory for capturing log entries in tests.
- Added unit tests for InMemoryChannel, covering constructor behavior, property assignments, channel communication, and disposal.
- Created InMemoryTransportOptionsTests to validate default values and customizable options for InMemory transport.
- Developed RabbitMqFrameProtocolTests to ensure correct parsing and property creation for RabbitMQ frames.
- Added RabbitMqTransportOptionsTests to verify default settings and customization options for RabbitMQ transport.
- Updated project files for testing libraries and dependencies.
This commit is contained in:
StellaOps Bot
2025-12-05 09:38:45 +02:00
parent 6a299d231f
commit 53508ceccb
98 changed files with 10868 additions and 663 deletions

View File

@@ -0,0 +1,463 @@
namespace StellaOps.Router.Common.Tests;
/// <summary>
/// Unit tests for <see cref="PathMatcher"/>.
/// </summary>
public sealed class PathMatcherTests
{
#region Constructor Tests
[Fact]
public void Constructor_SetsTemplate()
{
// Arrange & Act
var matcher = new PathMatcher("/api/users/{id}");
// Assert
matcher.Template.Should().Be("/api/users/{id}");
}
[Fact]
public void Constructor_DefaultsCaseInsensitive()
{
// Arrange & Act
var matcher = new PathMatcher("/api/Users");
// Assert
matcher.IsMatch("/api/users").Should().BeTrue();
}
[Fact]
public void Constructor_CaseSensitive_DoesNotMatchDifferentCase()
{
// Arrange & Act
var matcher = new PathMatcher("/api/Users", caseInsensitive: false);
// Assert
matcher.IsMatch("/api/users").Should().BeFalse();
matcher.IsMatch("/api/Users").Should().BeTrue();
}
#endregion
#region IsMatch Tests - Exact Paths
[Fact]
public void IsMatch_ExactPath_ReturnsTrue()
{
// Arrange
var matcher = new PathMatcher("/api/health");
// Act & Assert
matcher.IsMatch("/api/health").Should().BeTrue();
}
[Fact]
public void IsMatch_ExactPath_TrailingSlash_ReturnsTrue()
{
// Arrange
var matcher = new PathMatcher("/api/health");
// Act & Assert
matcher.IsMatch("/api/health/").Should().BeTrue();
}
[Fact]
public void IsMatch_ExactPath_NoLeadingSlash_ReturnsTrue()
{
// Arrange
var matcher = new PathMatcher("/api/health");
// Act & Assert
matcher.IsMatch("api/health").Should().BeTrue();
}
[Fact]
public void IsMatch_DifferentPath_ReturnsFalse()
{
// Arrange
var matcher = new PathMatcher("/api/health");
// Act & Assert
matcher.IsMatch("/api/status").Should().BeFalse();
}
[Fact]
public void IsMatch_PartialPath_ReturnsFalse()
{
// Arrange
var matcher = new PathMatcher("/api/users/list");
// Act & Assert
matcher.IsMatch("/api/users").Should().BeFalse();
}
[Fact]
public void IsMatch_LongerPath_ReturnsFalse()
{
// Arrange
var matcher = new PathMatcher("/api/users");
// Act & Assert
matcher.IsMatch("/api/users/list").Should().BeFalse();
}
#endregion
#region IsMatch Tests - Case Sensitivity
[Fact]
public void IsMatch_CaseInsensitive_MatchesMixedCase()
{
// Arrange
var matcher = new PathMatcher("/api/users", caseInsensitive: true);
// Act & Assert
matcher.IsMatch("/API/USERS").Should().BeTrue();
matcher.IsMatch("/Api/Users").Should().BeTrue();
matcher.IsMatch("/aPi/uSeRs").Should().BeTrue();
}
[Fact]
public void IsMatch_CaseSensitive_OnlyMatchesExactCase()
{
// Arrange
var matcher = new PathMatcher("/Api/Users", caseInsensitive: false);
// Act & Assert
matcher.IsMatch("/Api/Users").Should().BeTrue();
matcher.IsMatch("/api/users").Should().BeFalse();
matcher.IsMatch("/API/USERS").Should().BeFalse();
}
#endregion
#region TryMatch Tests - Single Parameter
[Fact]
public void TryMatch_SingleParameter_ReturnsTrue()
{
// Arrange
var matcher = new PathMatcher("/api/users/{id}");
// Act
var result = matcher.TryMatch("/api/users/123", out var parameters);
// Assert
result.Should().BeTrue();
}
[Fact]
public void TryMatch_SingleParameter_ExtractsParameter()
{
// Arrange
var matcher = new PathMatcher("/api/users/{id}");
// Act
matcher.TryMatch("/api/users/123", out var parameters);
// Assert
parameters.Should().ContainKey("id");
parameters["id"].Should().Be("123");
}
[Fact]
public void TryMatch_SingleParameter_ExtractsGuidParameter()
{
// Arrange
var matcher = new PathMatcher("/api/users/{userId}");
var guid = Guid.NewGuid().ToString();
// Act
matcher.TryMatch($"/api/users/{guid}", out var parameters);
// Assert
parameters["userId"].Should().Be(guid);
}
[Fact]
public void TryMatch_SingleParameter_ExtractsStringParameter()
{
// Arrange
var matcher = new PathMatcher("/api/users/{username}");
// Act
matcher.TryMatch("/api/users/john-doe", out var parameters);
// Assert
parameters["username"].Should().Be("john-doe");
}
#endregion
#region TryMatch Tests - Multiple Parameters
[Fact]
public void TryMatch_MultipleParameters_ReturnsTrue()
{
// Arrange
var matcher = new PathMatcher("/api/users/{userId}/posts/{postId}");
// Act
var result = matcher.TryMatch("/api/users/123/posts/456", out _);
// Assert
result.Should().BeTrue();
}
[Fact]
public void TryMatch_MultipleParameters_ExtractsAllParameters()
{
// Arrange
var matcher = new PathMatcher("/api/users/{userId}/posts/{postId}");
// Act
matcher.TryMatch("/api/users/user-1/posts/post-2", out var parameters);
// Assert
parameters.Should().ContainKey("userId");
parameters.Should().ContainKey("postId");
parameters["userId"].Should().Be("user-1");
parameters["postId"].Should().Be("post-2");
}
[Fact]
public void TryMatch_ThreeParameters_ExtractsAllParameters()
{
// Arrange
var matcher = new PathMatcher("/api/org/{orgId}/users/{userId}/roles/{roleId}");
// Act
matcher.TryMatch("/api/org/acme/users/john/roles/admin", out var parameters);
// Assert
parameters.Should().HaveCount(3);
parameters["orgId"].Should().Be("acme");
parameters["userId"].Should().Be("john");
parameters["roleId"].Should().Be("admin");
}
#endregion
#region TryMatch Tests - Non-Matching
[Fact]
public void TryMatch_NonMatchingPath_ReturnsFalse()
{
// Arrange
var matcher = new PathMatcher("/api/users/{id}");
// Act
var result = matcher.TryMatch("/api/posts/123", out var parameters);
// Assert
result.Should().BeFalse();
parameters.Should().BeEmpty();
}
[Fact]
public void TryMatch_MissingParameter_ReturnsFalse()
{
// Arrange
var matcher = new PathMatcher("/api/users/{id}/posts/{postId}");
// Act
var result = matcher.TryMatch("/api/users/123/posts", out var parameters);
// Assert
result.Should().BeFalse();
}
[Fact]
public void TryMatch_ExtraSegment_ReturnsFalse()
{
// Arrange
var matcher = new PathMatcher("/api/users/{id}");
// Act
var result = matcher.TryMatch("/api/users/123/extra", out _);
// Assert
result.Should().BeFalse();
}
#endregion
#region TryMatch Tests - Path Normalization
[Fact]
public void TryMatch_TrailingSlash_Matches()
{
// Arrange
var matcher = new PathMatcher("/api/users/{id}");
// Act
var result = matcher.TryMatch("/api/users/123/", out var parameters);
// Assert
result.Should().BeTrue();
parameters["id"].Should().Be("123");
}
[Fact]
public void TryMatch_NoLeadingSlash_Matches()
{
// Arrange
var matcher = new PathMatcher("/api/users/{id}");
// Act
var result = matcher.TryMatch("api/users/123", out var parameters);
// Assert
result.Should().BeTrue();
parameters["id"].Should().Be("123");
}
#endregion
#region TryMatch Tests - Parameter Type Constraints
[Fact]
public void TryMatch_ParameterWithTypeConstraint_ExtractsParameterName()
{
// Arrange
// The PathMatcher ignores type constraints but still extracts the parameter
var matcher = new PathMatcher("/api/users/{id:int}");
// Act
matcher.TryMatch("/api/users/123", out var parameters);
// Assert
parameters.Should().ContainKey("id");
parameters["id"].Should().Be("123");
}
[Fact]
public void TryMatch_ParameterWithGuidConstraint_ExtractsParameterName()
{
// Arrange
var matcher = new PathMatcher("/api/users/{id:guid}");
// Act
matcher.TryMatch("/api/users/abc-123", out var parameters);
// Assert
parameters.Should().ContainKey("id");
parameters["id"].Should().Be("abc-123");
}
#endregion
#region Edge Cases
[Fact]
public void TryMatch_RootPath_Matches()
{
// Arrange
var matcher = new PathMatcher("/");
// Act
var result = matcher.TryMatch("/", out var parameters);
// Assert
result.Should().BeTrue();
parameters.Should().BeEmpty();
}
[Fact]
public void TryMatch_SingleSegmentWithParameter_Matches()
{
// Arrange
var matcher = new PathMatcher("/{id}");
// Act
var result = matcher.TryMatch("/test-value", out var parameters);
// Assert
result.Should().BeTrue();
parameters["id"].Should().Be("test-value");
}
[Fact]
public void IsMatch_EmptyPath_HandlesGracefully()
{
// Arrange
var matcher = new PathMatcher("/");
// Act
var result = matcher.IsMatch("");
// Assert
result.Should().BeTrue();
}
[Fact]
public void TryMatch_ParameterWithHyphen_Extracts()
{
// Arrange
var matcher = new PathMatcher("/api/users/{user-id}");
// Act
matcher.TryMatch("/api/users/123", out var parameters);
// Assert
parameters.Should().ContainKey("user-id");
parameters["user-id"].Should().Be("123");
}
[Fact]
public void TryMatch_ParameterWithUnderscore_Extracts()
{
// Arrange
var matcher = new PathMatcher("/api/users/{user_id}");
// Act
matcher.TryMatch("/api/users/456", out var parameters);
// Assert
parameters.Should().ContainKey("user_id");
}
[Fact]
public void TryMatch_SpecialCharactersInPath_Matches()
{
// Arrange
var matcher = new PathMatcher("/api/search/{query}");
// Act
matcher.TryMatch("/api/search/hello-world_test.123", out var parameters);
// Assert
parameters["query"].Should().Be("hello-world_test.123");
}
[Fact]
public void IsMatch_ComplexRealWorldPath_Matches()
{
// Arrange
var matcher = new PathMatcher("/v1/organizations/{orgId}/projects/{projectId}/scans/{scanId}/vulnerabilities");
// Act
var result = matcher.IsMatch("/v1/organizations/acme-corp/projects/webapp/scans/scan-2024-001/vulnerabilities");
// Assert
result.Should().BeTrue();
}
[Fact]
public void TryMatch_ComplexRealWorldPath_ExtractsAllParameters()
{
// Arrange
var matcher = new PathMatcher("/v1/organizations/{orgId}/projects/{projectId}/scans/{scanId}");
// Act
matcher.TryMatch("/v1/organizations/acme-corp/projects/webapp/scans/scan-2024-001", out var parameters);
// Assert
parameters["orgId"].Should().Be("acme-corp");
parameters["projectId"].Should().Be("webapp");
parameters["scanId"].Should().Be("scan-2024-001");
}
#endregion
}