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.
464 lines
11 KiB
C#
464 lines
11 KiB
C#
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
|
|
}
|