This commit is contained in:
Stef Heyenrath
2026-02-12 08:00:10 +01:00
parent 3a266c3e18
commit e4ebdfba76
5 changed files with 110 additions and 57732 deletions
+1 -1
View File
@@ -254,6 +254,6 @@ paket-files/
./report/coverlet/ ./report/coverlet/
/test/WireMock.Net.Tests/coverage.opencover.xml /test/WireMock.Net.Tests/coverage.opencover.xml
/test/WireMock.Net.Tests/coverage.netcoreapp3.1.opencover.xml /test/WireMock.Net.Tests/coverage.netcoreapp3.1.opencover.xml
/test/WireMock.Net.Tests/coverage.net5.0.opencover.xml /test/WireMock.Net.Tests/coverage.net8.0.opencover.xml
*.received.* *.received.*
@@ -1,9 +1,11 @@
// Copyright © WireMock.Net // Copyright © WireMock.Net
using System.Net.WebSockets; using System.Net.WebSockets;
using Newtonsoft.Json;
using Stef.Validation; using Stef.Validation;
using WireMock.Matchers; using WireMock.Matchers;
using WireMock.Settings; using WireMock.Settings;
using WireMock.Transformers;
using WireMock.Types; using WireMock.Types;
namespace WireMock.WebSockets; namespace WireMock.WebSockets;
@@ -76,7 +78,7 @@ internal class WebSocketBuilder : IWebSocketBuilder
await Task.Delay(messageBuilder.Delay.Value); await Task.Delay(messageBuilder.Delay.Value);
} }
await SendMessageAsync(context, messageBuilder); await SendMessageAsync(this, context, messageBuilder, message);
}); });
} }
@@ -95,7 +97,7 @@ internal class WebSocketBuilder : IWebSocketBuilder
await Task.Delay(messageBuilder.Delay.Value); await Task.Delay(messageBuilder.Delay.Value);
} }
await SendMessageAsync(context, messageBuilder); await SendMessageAsync(this, context, messageBuilder, message);
} }
}); });
} }
@@ -213,7 +215,7 @@ internal class WebSocketBuilder : IWebSocketBuilder
await Task.Delay(messageBuilder.Delay.Value); await Task.Delay(messageBuilder.Delay.Value);
} }
await SendMessageAsync(context, messageBuilder); await SendMessageAsync(this, context, messageBuilder, message);
// If this message should close the connection, do it after sending // If this message should close the connection, do it after sending
if (messageBuilder.ShouldClose) if (messageBuilder.ShouldClose)
@@ -262,19 +264,60 @@ internal class WebSocketBuilder : IWebSocketBuilder
return false; return false;
} }
private static async Task SendMessageAsync(IWebSocketContext context, WebSocketMessageBuilder messageBuilder) private static async Task SendMessageAsync(WebSocketBuilder builder, IWebSocketContext context, WebSocketMessageBuilder messageBuilder, WebSocketMessage incomingMessage)
{ {
switch (messageBuilder.Type) switch (messageBuilder.Type)
{ {
case WebSocketMessageBuilder.MessageType.Text: case WebSocketMessageBuilder.MessageType.Text:
await context.SendAsync(messageBuilder.MessageText!); var text = messageBuilder.MessageText!;
if (builder.UseTransformer)
{
text = ApplyTransformer(builder, context, incomingMessage, text);
}
await context.SendAsync(text);
break; break;
case WebSocketMessageBuilder.MessageType.Bytes: case WebSocketMessageBuilder.MessageType.Bytes:
await context.SendAsync(messageBuilder.MessageBytes!); await context.SendAsync(messageBuilder.MessageBytes!);
break; break;
case WebSocketMessageBuilder.MessageType.Json: case WebSocketMessageBuilder.MessageType.Json:
await context.SendAsJsonAsync(messageBuilder.MessageData!); var jsonData = messageBuilder.MessageData!;
if (builder.UseTransformer)
{
var jsonString = JsonConvert.SerializeObject(jsonData);
jsonString = ApplyTransformer(builder, context, incomingMessage, jsonString);
jsonData = JsonConvert.DeserializeObject(jsonString) ?? jsonData;
}
await context.SendAsJsonAsync(jsonData);
break; break;
} }
} }
private static string ApplyTransformer(WebSocketBuilder builder, IWebSocketContext context, WebSocketMessage incomingMessage, string text)
{
try
{
if (incomingMessage == null)
{
// No incoming message, can't apply transformer
return text;
}
var transformer = TransformerFactory.Create(builder.TransformerType, context.Mapping.Settings);
var model = new WebSocketTransformModel
{
Mapping = context.Mapping,
Request = context.RequestMessage,
Message = incomingMessage,
Data = incomingMessage.MessageType == WebSocketMessageType.Text ? incomingMessage.Text : null
};
return transformer.Transform(text, model);
}
catch
{
// If transformation fails, return original text
return text;
}
}
} }
@@ -0,0 +1,29 @@
// Copyright © WireMock.Net
namespace WireMock.WebSockets;
/// <summary>
/// Model for WebSocket message transformation
/// </summary>
internal struct WebSocketTransformModel
{
/// <summary>
/// The mapping that matched this WebSocket request
/// </summary>
public IMapping Mapping { get; set; }
/// <summary>
/// The original request that initiated the WebSocket connection
/// </summary>
public IRequestMessage Request { get; set; }
/// <summary>
/// The incoming WebSocket message
/// </summary>
public WebSocketMessage Message { get; set; }
/// <summary>
/// The message data as string
/// </summary>
public string? Data { get; set; }
}
@@ -3,7 +3,6 @@
using System.Net; using System.Net;
using System.Net.Http; using System.Net.Http;
using System.Net.WebSockets; using System.Net.WebSockets;
using System.Text;
using FluentAssertions; using FluentAssertions;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using WireMock.Matchers; using WireMock.Matchers;
@@ -40,7 +39,7 @@ public class WebSocketIntegrationTests(ITestOutputHelper output)
); );
using var client = new ClientWebSocket(); using var client = new ClientWebSocket();
var uri = new Uri($"{server.Url!}/ws/echo"); var uri = new Uri($"{server.Url}/ws/echo");
// Act // Act
await client.ConnectAsync(uri, CancellationToken.None); await client.ConnectAsync(uri, CancellationToken.None);
@@ -80,7 +79,7 @@ public class WebSocketIntegrationTests(ITestOutputHelper output)
); );
using var client = new ClientWebSocket(); using var client = new ClientWebSocket();
var uri = new Uri($"{server.Url!}/ws/message"); var uri = new Uri($"{server.Url}/ws/message");
// Act // Act
await client.ConnectAsync(uri, CancellationToken.None); await client.ConnectAsync(uri, CancellationToken.None);
@@ -120,7 +119,7 @@ public class WebSocketIntegrationTests(ITestOutputHelper output)
); );
using var client = new ClientWebSocket(); using var client = new ClientWebSocket();
var uri = new Uri($"{server.Url!}/ws/message"); var uri = new Uri($"{server.Url}/ws/message");
await client.ConnectAsync(uri, CancellationToken.None); await client.ConnectAsync(uri, CancellationToken.None);
var testMessages = new[] { "First", "Second", "Third" }; var testMessages = new[] { "First", "Second", "Third" };
@@ -161,7 +160,7 @@ public class WebSocketIntegrationTests(ITestOutputHelper output)
); );
using var client = new ClientWebSocket(); using var client = new ClientWebSocket();
var uri = new Uri($"{server.Url!}/ws/binary"); var uri = new Uri($"{server.Url}/ws/binary");
// Act // Act
await client.ConnectAsync(uri, CancellationToken.None); await client.ConnectAsync(uri, CancellationToken.None);
@@ -201,7 +200,7 @@ public class WebSocketIntegrationTests(ITestOutputHelper output)
); );
using var client = new ClientWebSocket(); using var client = new ClientWebSocket();
var uri = new Uri($"{server.Url!}/ws/binary"); var uri = new Uri($"{server.Url}/ws/binary");
await client.ConnectAsync(uri, CancellationToken.None); await client.ConnectAsync(uri, CancellationToken.None);
var testMessages = new[] { "First", "Second", "Third" }; var testMessages = new[] { "First", "Second", "Third" };
@@ -335,7 +334,7 @@ public class WebSocketIntegrationTests(ITestOutputHelper output)
); );
using var client = new ClientWebSocket(); using var client = new ClientWebSocket();
var uri = new Uri($"{server.Url!}/ws/echo"); var uri = new Uri($"{server.Url}/ws/echo");
await client.ConnectAsync(uri, CancellationToken.None); await client.ConnectAsync(uri, CancellationToken.None);
var testMessages = new[] { "Hello", "World", "WebSocket", "Test" }; var testMessages = new[] { "Hello", "World", "WebSocket", "Test" };
@@ -373,7 +372,7 @@ public class WebSocketIntegrationTests(ITestOutputHelper output)
); );
using var client = new ClientWebSocket(); using var client = new ClientWebSocket();
var uri = new Uri($"{server.Url!}/ws/echo"); var uri = new Uri($"{server.Url}/ws/echo");
await client.ConnectAsync(uri, CancellationToken.None); await client.ConnectAsync(uri, CancellationToken.None);
var testData = new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05 }; var testData = new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05 };
@@ -409,7 +408,7 @@ public class WebSocketIntegrationTests(ITestOutputHelper output)
); );
using var client = new ClientWebSocket(); using var client = new ClientWebSocket();
var uri = new Uri($"{server.Url!}/ws/echo"); var uri = new Uri($"{server.Url}/ws/echo");
await client.ConnectAsync(uri, CancellationToken.None); await client.ConnectAsync(uri, CancellationToken.None);
// Act // Act
@@ -457,7 +456,7 @@ public class WebSocketIntegrationTests(ITestOutputHelper output)
); );
using var client = new ClientWebSocket(); using var client = new ClientWebSocket();
var uri = new Uri($"{server.Url!}/ws/chat"); var uri = new Uri($"{server.Url}/ws/chat");
await client.ConnectAsync(uri, CancellationToken.None); await client.ConnectAsync(uri, CancellationToken.None);
// Act // Act
@@ -529,7 +528,7 @@ public class WebSocketIntegrationTests(ITestOutputHelper output)
); );
using var client = new ClientWebSocket(); using var client = new ClientWebSocket();
var uri = new Uri($"{server.Url!}/ws/chat"); var uri = new Uri($"{server.Url}/ws/chat");
await client.ConnectAsync(uri, CancellationToken.None); await client.ConnectAsync(uri, CancellationToken.None);
var commands = new (string, Action<string>)[] var commands = new (string, Action<string>)[]
@@ -583,7 +582,7 @@ public class WebSocketIntegrationTests(ITestOutputHelper output)
); );
using var client = new ClientWebSocket(); using var client = new ClientWebSocket();
var uri = new Uri($"{server.Url!}/ws/conditional"); var uri = new Uri($"{server.Url}/ws/conditional");
await client.ConnectAsync(uri, CancellationToken.None); await client.ConnectAsync(uri, CancellationToken.None);
var testCases = new (string message, string expectedContains)[] var testCases = new (string message, string expectedContains)[]
@@ -630,7 +629,7 @@ public class WebSocketIntegrationTests(ITestOutputHelper output)
); );
using var client = new ClientWebSocket(); using var client = new ClientWebSocket();
var uri = new Uri($"{server.Url!}/ws/close"); var uri = new Uri($"{server.Url}/ws/close");
await client.ConnectAsync(uri, CancellationToken.None); await client.ConnectAsync(uri, CancellationToken.None);
// Act // Act
@@ -661,66 +660,42 @@ public class WebSocketIntegrationTests(ITestOutputHelper output)
} }
[Fact] [Fact]
public async Task Server_With_Multiple_Urls_Should_Handle_Http_And_WebSocket_In_Parallel() public async Task WithTransformer_Should_Transform_Message_Using_Handlebars()
{ {
// Arrange // Arrange
using var server = WireMockServer.Start(new WireMockServerSettings using var server = WireMockServer.Start(new WireMockServerSettings
{ {
Logger = new TestOutputHelperWireMockLogger(output), Logger = new TestOutputHelperWireMockLogger(output),
Urls = ["http://localhost:0", "ws://localhost:0"] Urls = ["ws://localhost:0"]
}); });
server server
.Given(Request.Create() .Given(Request.Create()
.WithPath("/api/test") .WithPath("/ws/transform")
.UsingGet()
)
.RespondWith(Response.Create()
.WithStatusCode(200)
.WithBody("OK")
);
server
.Given(Request.Create()
.WithPath("/ws/echo")
.WithWebSocketUpgrade() .WithWebSocketUpgrade()
) )
.RespondWith(Response.Create() .RespondWith(Response.Create()
.WithWebSocket(ws => ws.WithEcho()) .WithWebSocket(ws => ws
.WithTransformer()
.SendMessage(m => m.WithText("{{[String.Lowercase] message.Text}}"))
)
.WithTransformer()
); );
// Act & Assert
var httpTask = Task.Run(async () =>
{
using var httpClient = new HttpClient();
var response = await httpClient.GetAsync($"{server.Urls[0]}/api/test");
await Task.Delay(100);
response.StatusCode.Should().Be(HttpStatusCode.OK);
var content = await response.Content.ReadAsStringAsync();
content.Should().Be("OK");
});
var webSocketTask = Task.Run(async () =>
{
using var client = new ClientWebSocket(); using var client = new ClientWebSocket();
var uri = new Uri($"{server.Urls[1]}/ws/echo"); var uri = new Uri($"{server.Url}/ws/transform");
await client.ConnectAsync(uri, default); // Act
await client.ConnectAsync(uri, CancellationToken.None);
client.State.Should().Be(WebSocketState.Open); client.State.Should().Be(WebSocketState.Open);
var testMessage = "Hello from WebSocket"; var testMessage = "HellO";
await client.SendAsync(testMessage); await client.SendAsync(testMessage);
await Task.Delay(100); // Assert
var received = await client.ReceiveAsTextAsync(); var received = await client.ReceiveAsTextAsync();
received.Should().Be(testMessage); received.Should().Be("hello");
await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "Test complete", default); await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "Test complete", CancellationToken.None);
});
await Task.WhenAll(httpTask, webSocketTask);
} }
} }
File diff suppressed because it is too large Load Diff