using System.Text; using StellaOps.Router.Common.Enums; using StellaOps.Router.Common.Frames; using StellaOps.Router.Common.Models; using StellaOps.TestKit; namespace StellaOps.Router.Common.Tests; /// /// Unit tests for . /// public sealed class FrameConverterTests { #region ToFrame (RequestFrame) Tests [Trait("Category", TestCategories.Unit)] [Fact] public void ToFrame_RequestFrame_ReturnsFrameWithRequestType() { // Arrange var request = CreateTestRequestFrame(); // Act var frame = FrameConverter.ToFrame(request); // Assert frame.Type.Should().Be(FrameType.Request); } [Trait("Category", TestCategories.Unit)] [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"); } [Trait("Category", TestCategories.Unit)] [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"); } [Trait("Category", TestCategories.Unit)] [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 [Trait("Category", TestCategories.Unit)] [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(); } [Trait("Category", TestCategories.Unit)] [Fact] public void ToRequestFrame_WrongFrameType_ReturnsNull() { // Arrange var frame = new Frame { Type = FrameType.Response, CorrelationId = "test", Payload = Array.Empty() }; // Act var result = FrameConverter.ToRequestFrame(frame); // Assert result.Should().BeNull(); } [Trait("Category", TestCategories.Unit)] [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(); } [Trait("Category", TestCategories.Unit)] [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"); } [Trait("Category", TestCategories.Unit)] [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"); } [Trait("Category", TestCategories.Unit)] [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"); } [Trait("Category", TestCategories.Unit)] [Fact] public void ToRequestFrame_RoundTrip_PreservesHeaders() { // Arrange var headers = new Dictionary { ["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"); } [Trait("Category", TestCategories.Unit)] [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); } [Trait("Category", TestCategories.Unit)] [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); } [Trait("Category", TestCategories.Unit)] [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 [Trait("Category", TestCategories.Unit)] [Fact] public void ToFrame_ResponseFrame_ReturnsFrameWithResponseType() { // Arrange var response = CreateTestResponseFrame(); // Act var frame = FrameConverter.ToFrame(response); // Assert frame.Type.Should().Be(FrameType.Response); } [Trait("Category", TestCategories.Unit)] [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 [Trait("Category", TestCategories.Unit)] [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(); } [Trait("Category", TestCategories.Unit)] [Fact] public void ToResponseFrame_WrongFrameType_ReturnsNull() { // Arrange var frame = new Frame { Type = FrameType.Request, CorrelationId = "test", Payload = Array.Empty() }; // Act var result = FrameConverter.ToResponseFrame(frame); // Assert result.Should().BeNull(); } [Trait("Category", TestCategories.Unit)] [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(); } [Trait("Category", TestCategories.Unit)] [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"); } [Trait("Category", TestCategories.Unit)] [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); } [Trait("Category", TestCategories.Unit)] [Fact] public void ToResponseFrame_RoundTrip_PreservesHeaders() { // Arrange var headers = new Dictionary { ["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"); } [Trait("Category", TestCategories.Unit)] [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); } [Trait("Category", TestCategories.Unit)] [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 [Trait("Category", TestCategories.Unit)] [Fact] public void ToRequestFrame_EmptyPayload_ReturnsEmptyPayload() { // Arrange var originalRequest = new RequestFrame { RequestId = "test-id", Method = "GET", Path = "/test", Payload = Array.Empty() }; var frame = FrameConverter.ToFrame(originalRequest); // Act var result = FrameConverter.ToRequestFrame(frame); // Assert result!.Payload.IsEmpty.Should().BeTrue(); } [Trait("Category", TestCategories.Unit)] [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(); } [Trait("Category", TestCategories.Unit)] [Fact] public void ToResponseFrame_EmptyPayload_ReturnsEmptyPayload() { // Arrange var originalResponse = new ResponseFrame { RequestId = "test-id", StatusCode = 204, Payload = Array.Empty() }; var frame = FrameConverter.ToFrame(originalResponse); // Act var result = FrameConverter.ToResponseFrame(frame); // Assert result!.Payload.IsEmpty.Should().BeTrue(); } [Trait("Category", TestCategories.Unit)] [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 }