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,550 @@
using System.Text;
using StellaOps.Router.Common.Enums;
using StellaOps.Router.Common.Frames;
using StellaOps.Router.Common.Models;
namespace StellaOps.Router.Common.Tests;
/// <summary>
/// Unit tests for <see cref="FrameConverter"/>.
/// </summary>
public sealed class FrameConverterTests
{
#region ToFrame (RequestFrame) Tests
[Fact]
public void ToFrame_RequestFrame_ReturnsFrameWithRequestType()
{
// Arrange
var request = CreateTestRequestFrame();
// Act
var frame = FrameConverter.ToFrame(request);
// Assert
frame.Type.Should().Be(FrameType.Request);
}
[Fact]
public void ToFrame_RequestFrame_SetsCorrelationIdFromRequest()
{
// Arrange
var request = CreateTestRequestFrame(correlationId: "test-correlation-123");
// Act
var frame = FrameConverter.ToFrame(request);
// Assert
frame.CorrelationId.Should().Be("test-correlation-123");
}
[Fact]
public void ToFrame_RequestFrame_UsesRequestIdWhenCorrelationIdIsNull()
{
// Arrange
var request = new RequestFrame
{
RequestId = "request-id-456",
CorrelationId = null,
Method = "GET",
Path = "/test"
};
// Act
var frame = FrameConverter.ToFrame(request);
// Assert
frame.CorrelationId.Should().Be("request-id-456");
}
[Fact]
public void ToFrame_RequestFrame_SerializesPayload()
{
// Arrange
var request = CreateTestRequestFrame();
// Act
var frame = FrameConverter.ToFrame(request);
// Assert
frame.Payload.Length.Should().BeGreaterThan(0);
}
#endregion
#region ToRequestFrame Tests
[Fact]
public void ToRequestFrame_ValidRequestFrame_ReturnsRequestFrame()
{
// Arrange
var originalRequest = CreateTestRequestFrame();
var frame = FrameConverter.ToFrame(originalRequest);
// Act
var result = FrameConverter.ToRequestFrame(frame);
// Assert
result.Should().NotBeNull();
}
[Fact]
public void ToRequestFrame_WrongFrameType_ReturnsNull()
{
// Arrange
var frame = new Frame
{
Type = FrameType.Response,
CorrelationId = "test",
Payload = Array.Empty<byte>()
};
// Act
var result = FrameConverter.ToRequestFrame(frame);
// Assert
result.Should().BeNull();
}
[Fact]
public void ToRequestFrame_InvalidJson_ReturnsNull()
{
// Arrange
var frame = new Frame
{
Type = FrameType.Request,
CorrelationId = "test",
Payload = Encoding.UTF8.GetBytes("invalid json {{{")
};
// Act
var result = FrameConverter.ToRequestFrame(frame);
// Assert
result.Should().BeNull();
}
[Fact]
public void ToRequestFrame_RoundTrip_PreservesRequestId()
{
// Arrange
var originalRequest = CreateTestRequestFrame(requestId: "unique-request-id");
var frame = FrameConverter.ToFrame(originalRequest);
// Act
var result = FrameConverter.ToRequestFrame(frame);
// Assert
result!.RequestId.Should().Be("unique-request-id");
}
[Fact]
public void ToRequestFrame_RoundTrip_PreservesMethod()
{
// Arrange
var originalRequest = CreateTestRequestFrame(method: "DELETE");
var frame = FrameConverter.ToFrame(originalRequest);
// Act
var result = FrameConverter.ToRequestFrame(frame);
// Assert
result!.Method.Should().Be("DELETE");
}
[Fact]
public void ToRequestFrame_RoundTrip_PreservesPath()
{
// Arrange
var originalRequest = CreateTestRequestFrame(path: "/api/users/123");
var frame = FrameConverter.ToFrame(originalRequest);
// Act
var result = FrameConverter.ToRequestFrame(frame);
// Assert
result!.Path.Should().Be("/api/users/123");
}
[Fact]
public void ToRequestFrame_RoundTrip_PreservesHeaders()
{
// Arrange
var headers = new Dictionary<string, string>
{
["Content-Type"] = "application/json",
["X-Custom-Header"] = "custom-value"
};
var originalRequest = new RequestFrame
{
RequestId = "test-id",
Method = "POST",
Path = "/test",
Headers = headers
};
var frame = FrameConverter.ToFrame(originalRequest);
// Act
var result = FrameConverter.ToRequestFrame(frame);
// Assert
result!.Headers.Should().ContainKey("Content-Type");
result.Headers["Content-Type"].Should().Be("application/json");
result.Headers["X-Custom-Header"].Should().Be("custom-value");
}
[Fact]
public void ToRequestFrame_RoundTrip_PreservesPayload()
{
// Arrange
var payloadBytes = Encoding.UTF8.GetBytes("{\"key\":\"value\"}");
var originalRequest = new RequestFrame
{
RequestId = "test-id",
Method = "POST",
Path = "/test",
Payload = payloadBytes
};
var frame = FrameConverter.ToFrame(originalRequest);
// Act
var result = FrameConverter.ToRequestFrame(frame);
// Assert
result!.Payload.ToArray().Should().BeEquivalentTo(payloadBytes);
}
[Fact]
public void ToRequestFrame_RoundTrip_PreservesTimeoutSeconds()
{
// Arrange
var originalRequest = new RequestFrame
{
RequestId = "test-id",
Method = "GET",
Path = "/test",
TimeoutSeconds = 60
};
var frame = FrameConverter.ToFrame(originalRequest);
// Act
var result = FrameConverter.ToRequestFrame(frame);
// Assert
result!.TimeoutSeconds.Should().Be(60);
}
[Fact]
public void ToRequestFrame_RoundTrip_PreservesSupportsStreaming()
{
// Arrange
var originalRequest = new RequestFrame
{
RequestId = "test-id",
Method = "GET",
Path = "/test",
SupportsStreaming = true
};
var frame = FrameConverter.ToFrame(originalRequest);
// Act
var result = FrameConverter.ToRequestFrame(frame);
// Assert
result!.SupportsStreaming.Should().BeTrue();
}
#endregion
#region ToFrame (ResponseFrame) Tests
[Fact]
public void ToFrame_ResponseFrame_ReturnsFrameWithResponseType()
{
// Arrange
var response = CreateTestResponseFrame();
// Act
var frame = FrameConverter.ToFrame(response);
// Assert
frame.Type.Should().Be(FrameType.Response);
}
[Fact]
public void ToFrame_ResponseFrame_SetsCorrelationIdToRequestId()
{
// Arrange
var response = CreateTestResponseFrame(requestId: "req-123");
// Act
var frame = FrameConverter.ToFrame(response);
// Assert
frame.CorrelationId.Should().Be("req-123");
}
#endregion
#region ToResponseFrame Tests
[Fact]
public void ToResponseFrame_ValidResponseFrame_ReturnsResponseFrame()
{
// Arrange
var originalResponse = CreateTestResponseFrame();
var frame = FrameConverter.ToFrame(originalResponse);
// Act
var result = FrameConverter.ToResponseFrame(frame);
// Assert
result.Should().NotBeNull();
}
[Fact]
public void ToResponseFrame_WrongFrameType_ReturnsNull()
{
// Arrange
var frame = new Frame
{
Type = FrameType.Request,
CorrelationId = "test",
Payload = Array.Empty<byte>()
};
// Act
var result = FrameConverter.ToResponseFrame(frame);
// Assert
result.Should().BeNull();
}
[Fact]
public void ToResponseFrame_InvalidJson_ReturnsNull()
{
// Arrange
var frame = new Frame
{
Type = FrameType.Response,
CorrelationId = "test",
Payload = Encoding.UTF8.GetBytes("not valid json")
};
// Act
var result = FrameConverter.ToResponseFrame(frame);
// Assert
result.Should().BeNull();
}
[Fact]
public void ToResponseFrame_RoundTrip_PreservesRequestId()
{
// Arrange
var originalResponse = CreateTestResponseFrame(requestId: "original-req-id");
var frame = FrameConverter.ToFrame(originalResponse);
// Act
var result = FrameConverter.ToResponseFrame(frame);
// Assert
result!.RequestId.Should().Be("original-req-id");
}
[Fact]
public void ToResponseFrame_RoundTrip_PreservesStatusCode()
{
// Arrange
var originalResponse = CreateTestResponseFrame(statusCode: 404);
var frame = FrameConverter.ToFrame(originalResponse);
// Act
var result = FrameConverter.ToResponseFrame(frame);
// Assert
result!.StatusCode.Should().Be(404);
}
[Fact]
public void ToResponseFrame_RoundTrip_PreservesHeaders()
{
// Arrange
var headers = new Dictionary<string, string>
{
["Content-Type"] = "application/json",
["Cache-Control"] = "no-cache"
};
var originalResponse = new ResponseFrame
{
RequestId = "test-id",
StatusCode = 200,
Headers = headers
};
var frame = FrameConverter.ToFrame(originalResponse);
// Act
var result = FrameConverter.ToResponseFrame(frame);
// Assert
result!.Headers["Content-Type"].Should().Be("application/json");
result.Headers["Cache-Control"].Should().Be("no-cache");
}
[Fact]
public void ToResponseFrame_RoundTrip_PreservesPayload()
{
// Arrange
var payloadBytes = Encoding.UTF8.GetBytes("{\"result\":\"success\"}");
var originalResponse = new ResponseFrame
{
RequestId = "test-id",
StatusCode = 200,
Payload = payloadBytes
};
var frame = FrameConverter.ToFrame(originalResponse);
// Act
var result = FrameConverter.ToResponseFrame(frame);
// Assert
result!.Payload.ToArray().Should().BeEquivalentTo(payloadBytes);
}
[Fact]
public void ToResponseFrame_RoundTrip_PreservesHasMoreChunks()
{
// Arrange
var originalResponse = new ResponseFrame
{
RequestId = "test-id",
StatusCode = 200,
HasMoreChunks = true
};
var frame = FrameConverter.ToFrame(originalResponse);
// Act
var result = FrameConverter.ToResponseFrame(frame);
// Assert
result!.HasMoreChunks.Should().BeTrue();
}
#endregion
#region Edge Cases
[Fact]
public void ToRequestFrame_EmptyPayload_ReturnsEmptyPayload()
{
// Arrange
var originalRequest = new RequestFrame
{
RequestId = "test-id",
Method = "GET",
Path = "/test",
Payload = Array.Empty<byte>()
};
var frame = FrameConverter.ToFrame(originalRequest);
// Act
var result = FrameConverter.ToRequestFrame(frame);
// Assert
result!.Payload.IsEmpty.Should().BeTrue();
}
[Fact]
public void ToRequestFrame_NullHeaders_ReturnsEmptyHeaders()
{
// Arrange
var originalRequest = new RequestFrame
{
RequestId = "test-id",
Method = "GET",
Path = "/test"
};
var frame = FrameConverter.ToFrame(originalRequest);
// Act
var result = FrameConverter.ToRequestFrame(frame);
// Assert
result!.Headers.Should().NotBeNull();
result.Headers.Should().BeEmpty();
}
[Fact]
public void ToResponseFrame_EmptyPayload_ReturnsEmptyPayload()
{
// Arrange
var originalResponse = new ResponseFrame
{
RequestId = "test-id",
StatusCode = 204,
Payload = Array.Empty<byte>()
};
var frame = FrameConverter.ToFrame(originalResponse);
// Act
var result = FrameConverter.ToResponseFrame(frame);
// Assert
result!.Payload.IsEmpty.Should().BeTrue();
}
[Fact]
public void ToFrame_LargePayload_Succeeds()
{
// Arrange
var largePayload = new byte[1024 * 1024]; // 1MB
Random.Shared.NextBytes(largePayload);
var originalRequest = new RequestFrame
{
RequestId = "test-id",
Method = "POST",
Path = "/upload",
Payload = largePayload
};
// Act
var frame = FrameConverter.ToFrame(originalRequest);
var result = FrameConverter.ToRequestFrame(frame);
// Assert
result.Should().NotBeNull();
result!.Payload.ToArray().Should().BeEquivalentTo(largePayload);
}
#endregion
#region Helper Methods
private static RequestFrame CreateTestRequestFrame(
string? requestId = null,
string? correlationId = null,
string method = "GET",
string path = "/test")
{
return new RequestFrame
{
RequestId = requestId ?? Guid.NewGuid().ToString("N"),
CorrelationId = correlationId,
Method = method,
Path = path
};
}
private static ResponseFrame CreateTestResponseFrame(
string? requestId = null,
int statusCode = 200)
{
return new ResponseFrame
{
RequestId = requestId ?? Guid.NewGuid().ToString("N"),
StatusCode = statusCode
};
}
#endregion
}

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
}

View File

@@ -0,0 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<LangVersion>preview</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<!-- Suppress CA2255 from OpenSSL auto-init shim included via Directory.Build.props -->
<NoWarn>$(NoWarn);CA2255</NoWarn>
<IsPackable>false</IsPackable>
<RootNamespace>StellaOps.Router.Common.Tests</RootNamespace>
<!-- Disable Concelier test infrastructure (Mongo2Go, etc.) since not needed for Router tests -->
<UseConcelierTestInfra>false</UseConcelierTestInfra>
</PropertyGroup>
<ItemGroup>
<Using Include="Xunit" />
<Using Include="FluentAssertions" />
</ItemGroup>
<ItemGroup>
<!-- Test SDK packages come from Directory.Build.props -->
<PackageReference Include="FluentAssertions" Version="6.12.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\StellaOps.Router.Common\StellaOps.Router.Common.csproj" />
<ProjectReference Include="..\StellaOps.Router.Testing\StellaOps.Router.Testing.csproj" />
</ItemGroup>
</Project>