Add tests

This commit is contained in:
Stef Heyenrath
2026-02-10 07:39:06 +01:00
parent f53afec823
commit 1d2b22545b
7 changed files with 133 additions and 82 deletions

View File

@@ -15,6 +15,7 @@
<PackageReadmeFile>PackageReadme.md</PackageReadmeFile>
<LangVersion>12</LangVersion>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<PropertyGroup Condition="'$(TF_BUILD)' == 'true'">

View File

@@ -1,7 +1,5 @@
// Copyright © WireMock.Net
using System;
namespace WireMock.Types;
[Flags]
@@ -13,5 +11,11 @@ public enum HostingScheme
Https = 0x2,
HttpAndHttps = Http | Https
HttpAndHttps = Http | Https,
Ws = 0x4,
Wss = 0x8,
WsAndWss = Ws | Wss
}

View File

@@ -1,10 +1,5 @@
// Copyright © WireMock.Net
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
@@ -27,9 +22,9 @@ internal partial class AspNetCoreSelfHost : IOwinSelfHost
public bool IsStarted { get; private set; }
public List<string> Urls { get; } = new();
public List<string> Urls { get; } = [];
public List<int> Ports { get; } = new();
public List<int> Ports { get; } = [];
public Exception? RunningException { get; private set; }
@@ -120,14 +115,42 @@ internal partial class AspNetCoreSelfHost : IOwinSelfHost
{
var addresses = _host.ServerFeatures
.Get<Microsoft.AspNetCore.Hosting.Server.Features.IServerAddressesFeature>()!
.Addresses;
.Addresses
.ToArray();
foreach (var address in addresses)
if (_urlOptions.Urls == null)
{
Urls.Add(address.Replace("0.0.0.0", "localhost").Replace("[::]", "localhost"));
foreach (var address in addresses)
{
PortUtils.TryExtract(address, out _, out _, out var scheme, out var host, out var port);
PortUtils.TryExtract(address, out _, out _, out _, out _, out var port);
Ports.Add(port);
var replacedHost = ReplaceHostWithLocalhost(host!);
var newUrl = $"{scheme}://{replacedHost}:{port}";
Urls.Add(newUrl);
Ports.Add(port);
}
}
else
{
var urlOptions = _urlOptions?.Urls?.ToArray() ?? [];
for (int i = 0; i < urlOptions.Length; i++)
{
PortUtils.TryExtract(urlOptions[i], out _, out _, out var originalScheme, out _, out _);
if (originalScheme!.StartsWith("grpc", StringComparison.OrdinalIgnoreCase))
{
// Always replace "grpc" with "http" in the scheme because GrpcChannel needs http or https.
originalScheme = originalScheme.Replace("grpc", "http", StringComparison.OrdinalIgnoreCase);
}
PortUtils.TryExtract(addresses[i], out _, out _, out _, out var realHost, out var realPort);
var replacedHost = ReplaceHostWithLocalhost(realHost!);
var newUrl = $"{originalScheme}://{replacedHost}:{realPort}";
Urls.Add(newUrl);
Ports.Add(realPort);
}
}
IsStarted = true;
@@ -159,4 +182,9 @@ internal partial class AspNetCoreSelfHost : IOwinSelfHost
IsStarted = false;
return _host.StopAsync();
}
private static string ReplaceHostWithLocalhost(string host)
{
return host.Replace("0.0.0.0", "localhost").Replace("[::]", "localhost");
}
}

View File

@@ -1,6 +1,5 @@
// Copyright © WireMock.Net
using System.Collections.Generic;
using WireMock.Types;
using WireMock.Util;
@@ -23,20 +22,34 @@ internal class HostUrlOptions
var list = new List<HostUrlDetails>();
if (Urls == null)
{
if (HostingScheme is HostingScheme.Http or HostingScheme.Https)
if (HostingScheme is not HostingScheme.None)
{
var port = Port > 0 ? Port.Value : FindFreeTcpPort();
var scheme = HostingScheme == HostingScheme.Https ? "https" : "http";
list.Add(new HostUrlDetails { IsHttps = HostingScheme == HostingScheme.Https, IsHttp2 = UseHttp2 == true, Url = $"{scheme}://{Star}:{port}", Scheme = scheme, Host = Star, Port = port });
var scheme = GetSchemeAsString(HostingScheme);
var port = Port > 0 ? Port.Value : 0;
var isHttps = HostingScheme == HostingScheme.Https || HostingScheme == HostingScheme.Wss;
list.Add(new HostUrlDetails { IsHttps = isHttps, IsHttp2 = UseHttp2 == true, Url = $"{scheme}://{Star}:{port}", Scheme = scheme, Host = Star, Port = port });
}
if (HostingScheme == HostingScheme.HttpAndHttps)
{
var httpPort = Port > 0 ? Port.Value : FindFreeTcpPort();
list.Add(new HostUrlDetails { IsHttps = false, IsHttp2 = UseHttp2 == true, Url = $"http://{Star}:{httpPort}", Scheme = "http", Host = Star, Port = httpPort });
var port = Port > 0 ? Port.Value : 0;
var scheme = GetSchemeAsString(HostingScheme.Http);
list.Add(new HostUrlDetails { IsHttps = false, IsHttp2 = UseHttp2 == true, Url = $"{scheme}://{Star}:{port}", Scheme = scheme, Host = Star, Port = port });
var httpsPort = FindFreeTcpPort(); // In this scenario, always get a free port for https.
list.Add(new HostUrlDetails { IsHttps = true, IsHttp2 = UseHttp2 == true, Url = $"https://{Star}:{httpsPort}", Scheme = "https", Host = Star, Port = httpsPort });
var securePort = 0; // In this scenario, always get a free port for https.
var secureScheme = GetSchemeAsString(HostingScheme.Https);
list.Add(new HostUrlDetails { IsHttps = true, IsHttp2 = UseHttp2 == true, Url = $"{secureScheme}://{Star}:{securePort}", Scheme = secureScheme, Host = Star, Port = securePort });
}
if (HostingScheme == HostingScheme.WsAndWss)
{
var port = Port > 0 ? Port.Value : 0;
var scheme = GetSchemeAsString(HostingScheme.Ws);
list.Add(new HostUrlDetails { IsHttps = false, IsHttp2 = UseHttp2 == true, Url = $"{scheme}://{Star}:{port}", Scheme = scheme, Host = Star, Port = port });
var securePort = 0; // In this scenario, always get a free port for https.
var secureScheme = GetSchemeAsString(HostingScheme.Wss);
list.Add(new HostUrlDetails { IsHttps = true, IsHttp2 = UseHttp2 == true, Url = $"{secureScheme}://{Star}:{securePort}", Scheme = secureScheme, Host = Star, Port = securePort });
}
}
else
@@ -53,12 +66,19 @@ internal class HostUrlOptions
return list;
}
private static int FindFreeTcpPort()
private string GetSchemeAsString(HostingScheme scheme)
{
//#if USE_ASPNETCORE || NETSTANDARD2_0 || NETSTANDARD2_1
return 0;
//#else
//return PortUtils.FindFreeTcpPort();
//#endif
return scheme switch
{
HostingScheme.Http => "http",
HostingScheme.Https => "https",
HostingScheme.HttpAndHttps => "http", // Default to http when both are specified, since the https URL will be added separately with a free port.
HostingScheme.Ws => "ws",
HostingScheme.Wss => "wss",
HostingScheme.WsAndWss => "ws", // Default to ws when both are specified, since the wss URL will be added separately with a free port.
_ => throw new NotSupportedException($"Unsupported hosting scheme: {HostingScheme}")
};
}
}

View File

@@ -1,9 +1,6 @@
// Copyright © WireMock.Net
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text.RegularExpressions;
@@ -16,7 +13,7 @@ namespace WireMock.Util;
/// </summary>
internal static class PortUtils
{
private static readonly Regex UrlDetailsRegex = new(@"^((?<proto>\w+)://)(?<host>[^/]+?):(?<port>\d+)\/?$", RegexOptions.Compiled, RegexConstants.DefaultTimeout);
private static readonly Regex UrlDetailsRegex = new(@"^((?<scheme>\w+)://)(?<host>[^/]+?):(?<port>\d+)\/?$", RegexOptions.Compiled, RegexConstants.DefaultTimeout);
/// <summary>
/// Finds a random, free port to be listened on.
@@ -37,9 +34,7 @@ internal static class PortUtils
}
finally
{
//#if !NETSTANDARD1_3
portSocket.Close();
//#endif
portSocket.Dispose();
}
}
@@ -75,9 +70,7 @@ internal static class PortUtils
{
foreach (var socket in sockets)
{
//#if !NETSTANDARD1_3
socket.Close();
//#endif
socket.Dispose();
}
}
@@ -97,8 +90,8 @@ internal static class PortUtils
var match = UrlDetailsRegex.Match(url);
if (match.Success)
{
scheme = match.Groups["proto"].Value;
isHttps = scheme.StartsWith("https", StringComparison.OrdinalIgnoreCase) || scheme.StartsWith("grpcs", StringComparison.OrdinalIgnoreCase);
scheme = match.Groups["scheme"].Value;
isHttps = scheme.StartsWith("https", StringComparison.OrdinalIgnoreCase) || scheme.StartsWith("grpcs", StringComparison.OrdinalIgnoreCase) || scheme.StartsWith("wss", StringComparison.OrdinalIgnoreCase);
isHttp2 = scheme.StartsWith("grpc", StringComparison.OrdinalIgnoreCase);
host = match.Groups["host"].Value;

View File

@@ -64,16 +64,14 @@ internal class WebSocketBuilder : IWebSocketBuilder
return this;
}
public IWebSocketBuilder WithMessageHandler(
Func<WebSocketMessage, IWebSocketContext, Task> handler)
public IWebSocketBuilder WithMessageHandler(Func<WebSocketMessage, IWebSocketContext, Task> handler)
{
MessageHandler = Guard.NotNull(handler);
IsEcho = false; // Disable echo if custom handler is set
return this;
}
public IWebSocketBuilder WithMessageSequence(
Action<IWebSocketMessageSequenceBuilder> configure)
public IWebSocketBuilder WithMessageSequence(Action<IWebSocketMessageSequenceBuilder> configure)
{
var sequenceBuilder = new WebSocketMessageSequenceBuilder();
configure(sequenceBuilder);

View File

@@ -1,12 +1,7 @@
// Copyright © WireMock.Net
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using FluentAssertions;
using Newtonsoft.Json.Linq;
using WireMock.Net.Xunit;
@@ -14,7 +9,6 @@ using WireMock.RequestBuilders;
using WireMock.ResponseBuilders;
using WireMock.Server;
using WireMock.Settings;
using Xunit;
using Xunit.Abstractions;
namespace WireMock.Net.Tests.WebSockets;
@@ -34,7 +28,8 @@ public class WebSocketIntegrationTests
// Arrange
using var server = WireMockServer.Start(new WireMockServerSettings
{
Logger = new TestOutputHelperWireMockLogger(_output)
Logger = new TestOutputHelperWireMockLogger(_output),
Urls = ["ws://localhost:0"]
});
server
@@ -43,14 +38,13 @@ public class WebSocketIntegrationTests
.WithWebSocketUpgrade()
)
.RespondWith(Response.Create()
.WithHeader("x", "y")
.WithWebSocket(ws => ws
.WithEcho()
)
);
using var client = new ClientWebSocket();
var uri = new Uri($"{server.Urls[0].Replace("http://", "ws://")}/ws/echo");
var uri = new Uri($"{server.Url!}/ws/echo");
// Act
await client.ConnectAsync(uri, CancellationToken.None);
@@ -78,7 +72,8 @@ public class WebSocketIntegrationTests
// Arrange
using var server = WireMockServer.Start(new WireMockServerSettings
{
Logger = new TestOutputHelperWireMockLogger(_output)
Logger = new TestOutputHelperWireMockLogger(_output),
Urls = ["ws://localhost:0"]
});
server
@@ -91,7 +86,7 @@ public class WebSocketIntegrationTests
);
using var client = new ClientWebSocket();
var uri = new Uri($"{server.Urls[0].Replace("http://", "ws://")}/ws/echo");
var uri = new Uri($"{server.Url!}/ws/echo");
await client.ConnectAsync(uri, CancellationToken.None);
var testMessages = new[] { "Hello", "World", "WebSocket", "Test" };
@@ -118,7 +113,8 @@ public class WebSocketIntegrationTests
// Arrange
using var server = WireMockServer.Start(new WireMockServerSettings
{
Logger = new TestOutputHelperWireMockLogger(_output)
Logger = new TestOutputHelperWireMockLogger(_output),
Urls = ["ws://localhost:0"]
});
server
@@ -131,7 +127,7 @@ public class WebSocketIntegrationTests
);
using var client = new ClientWebSocket();
var uri = new Uri($"{server.Urls[0].Replace("http://", "ws://")}/ws/echo");
var uri = new Uri($"{server.Url!}/ws/echo");
await client.ConnectAsync(uri, CancellationToken.None);
var testData = new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05 };
@@ -157,7 +153,8 @@ public class WebSocketIntegrationTests
// Arrange
using var server = WireMockServer.Start(new WireMockServerSettings
{
Logger = new TestOutputHelperWireMockLogger(_output)
Logger = new TestOutputHelperWireMockLogger(_output),
Urls = ["ws://localhost:0"]
});
server
@@ -170,7 +167,7 @@ public class WebSocketIntegrationTests
);
using var client = new ClientWebSocket();
var uri = new Uri($"{server.Urls[0].Replace("http://", "ws://")}/ws/echo");
var uri = new Uri($"{server.Url!}/ws/echo");
await client.ConnectAsync(uri, CancellationToken.None);
// Act
@@ -192,7 +189,8 @@ public class WebSocketIntegrationTests
// Arrange
using var server = WireMockServer.Start(new WireMockServerSettings
{
Logger = new TestOutputHelperWireMockLogger(_output)
Logger = new TestOutputHelperWireMockLogger(_output),
Urls = ["ws://localhost:0"]
});
server
@@ -218,7 +216,7 @@ public class WebSocketIntegrationTests
);
using var client = new ClientWebSocket();
var uri = new Uri($"{server.Urls[0].Replace("http://", "ws://")}/ws/chat");
var uri = new Uri($"{server.Url!}/ws/chat");
await client.ConnectAsync(uri, CancellationToken.None);
// Act
@@ -244,7 +242,8 @@ public class WebSocketIntegrationTests
// Arrange
using var server = WireMockServer.Start(new WireMockServerSettings
{
Logger = new TestOutputHelperWireMockLogger(_output)
Logger = new TestOutputHelperWireMockLogger(_output),
Urls = ["ws://localhost:0"]
});
server
@@ -288,7 +287,7 @@ public class WebSocketIntegrationTests
);
using var client = new ClientWebSocket();
var uri = new Uri($"{server.Urls[0].Replace("http://", "ws://")}/ws/chat");
var uri = new Uri($"{server.Url!}/ws/chat");
await client.ConnectAsync(uri, CancellationToken.None);
var commands = new (string, Action<string>)[]
@@ -322,7 +321,8 @@ public class WebSocketIntegrationTests
// Arrange
using var server = WireMockServer.Start(new WireMockServerSettings
{
Logger = new TestOutputHelperWireMockLogger(_output)
Logger = new TestOutputHelperWireMockLogger(_output),
Urls = ["ws://localhost:0"]
});
server
@@ -348,7 +348,7 @@ public class WebSocketIntegrationTests
);
using var client = new ClientWebSocket();
var uri = new Uri($"{server.Urls[0].Replace("http://", "ws://")}/ws/json");
var uri = new Uri($"{server.Url!}/ws/json");
await client.ConnectAsync(uri, CancellationToken.None);
// Act
@@ -362,7 +362,7 @@ public class WebSocketIntegrationTests
// Assert
result.MessageType.Should().Be(WebSocketMessageType.Text);
var json = JObject.Parse(received);
json["message"]!.ToString().Should().Be(testMessage);
json["length"]!.Value<int>().Should().Be(testMessage.Length);
@@ -378,7 +378,8 @@ public class WebSocketIntegrationTests
// Arrange
using var server = WireMockServer.Start(new WireMockServerSettings
{
Logger = new TestOutputHelperWireMockLogger(_output)
Logger = new TestOutputHelperWireMockLogger(_output),
Urls = ["ws://localhost:0"]
});
server
@@ -404,7 +405,7 @@ public class WebSocketIntegrationTests
);
using var client = new ClientWebSocket();
var uri = new Uri($"{server.Urls[0].Replace("http://", "ws://")}/ws/json");
var uri = new Uri($"{server.Url!}/ws/json");
await client.ConnectAsync(uri, CancellationToken.None);
var testMessages = new[] { "First", "Second", "Third" };
@@ -433,7 +434,8 @@ public class WebSocketIntegrationTests
// Arrange
using var server = WireMockServer.Start(new WireMockServerSettings
{
Logger = new TestOutputHelperWireMockLogger(_output)
Logger = new TestOutputHelperWireMockLogger(_output),
Urls = ["ws://localhost:0"]
});
server
@@ -470,7 +472,7 @@ public class WebSocketIntegrationTests
);
using var client = new ClientWebSocket();
var uri = new Uri($"{server.Urls[0].Replace("http://", "ws://")}/ws/json");
var uri = new Uri($"{server.Url!}/ws/json");
await client.ConnectAsync(uri, CancellationToken.None);
// Act
@@ -499,7 +501,8 @@ public class WebSocketIntegrationTests
// Arrange
using var server = WireMockServer.Start(new WireMockServerSettings
{
Logger = new TestOutputHelperWireMockLogger(_output)
Logger = new TestOutputHelperWireMockLogger(_output),
Urls = ["ws://localhost:0"]
});
var broadcastMappingGuid = Guid.NewGuid();
@@ -533,7 +536,7 @@ public class WebSocketIntegrationTests
using var client2 = new ClientWebSocket();
using var client3 = new ClientWebSocket();
var uri = new Uri($"{server.Urls[0].Replace("http://", "ws://")}/ws/broadcast");
var uri = new Uri($"{server.Url!}/ws/broadcast");
await client1.ConnectAsync(uri, CancellationToken.None);
await client2.ConnectAsync(uri, CancellationToken.None);
@@ -579,7 +582,8 @@ public class WebSocketIntegrationTests
// Arrange
using var server = WireMockServer.Start(new WireMockServerSettings
{
Logger = new TestOutputHelperWireMockLogger(_output)
Logger = new TestOutputHelperWireMockLogger(_output),
Urls = ["ws://localhost:0"]
});
var broadcastMappingGuid = Guid.NewGuid();
@@ -606,7 +610,7 @@ public class WebSocketIntegrationTests
using var client1 = new ClientWebSocket();
using var client2 = new ClientWebSocket();
var uri = new Uri($"{server.Urls[0].Replace("http://", "ws://")}/ws/broadcast");
var uri = new Uri($"{server.Url!}/ws/broadcast");
await client1.ConnectAsync(uri, CancellationToken.None);
await client2.ConnectAsync(uri, CancellationToken.None);
@@ -639,7 +643,8 @@ public class WebSocketIntegrationTests
// Arrange
using var server = WireMockServer.Start(new WireMockServerSettings
{
Logger = new TestOutputHelperWireMockLogger(_output)
Logger = new TestOutputHelperWireMockLogger(_output),
Urls = ["ws://localhost:0"]
});
var broadcastMappingGuid = Guid.NewGuid();
@@ -673,7 +678,7 @@ public class WebSocketIntegrationTests
using var client1 = new ClientWebSocket();
using var client2 = new ClientWebSocket();
var uri = new Uri($"{server.Urls[0].Replace("http://", "ws://")}/ws/broadcast-json");
var uri = new Uri($"{server.Url!}/ws/broadcast-json");
await client1.ConnectAsync(uri, CancellationToken.None);
await client2.ConnectAsync(uri, CancellationToken.None);
@@ -717,7 +722,8 @@ public class WebSocketIntegrationTests
// Arrange
using var server = WireMockServer.Start(new WireMockServerSettings
{
Logger = new TestOutputHelperWireMockLogger(_output)
Logger = new TestOutputHelperWireMockLogger(_output),
Urls = ["ws://localhost:0"]
});
var broadcastMappingGuid = Guid.NewGuid();
@@ -746,7 +752,7 @@ public class WebSocketIntegrationTests
using var client1 = new ClientWebSocket();
using var client2 = new ClientWebSocket();
var uri = new Uri($"{server.Urls[0].Replace("http://", "ws://")}/ws/broadcast");
var uri = new Uri($"{server.Url!}/ws/broadcast");
await client1.ConnectAsync(uri, CancellationToken.None);
await client2.ConnectAsync(uri, CancellationToken.None);
@@ -784,7 +790,8 @@ public class WebSocketIntegrationTests
// Arrange
using var server = WireMockServer.Start(new WireMockServerSettings
{
Logger = new TestOutputHelperWireMockLogger(_output)
Logger = new TestOutputHelperWireMockLogger(_output),
Urls = ["ws://localhost:0"]
});
var broadcastMappingGuid = Guid.NewGuid();
@@ -808,8 +815,8 @@ public class WebSocketIntegrationTests
)
);
var uri = new Uri($"{server.Urls[0].Replace("http://", "ws://")}/ws/broadcast");
const int clientCount = 5;
var uri = new Uri($"{server.Url!}/ws/broadcast");
const int clientCount = 3;
var clients = new List<ClientWebSocket>();
try
@@ -822,7 +829,7 @@ public class WebSocketIntegrationTests
clients.Add(client);
}
await Task.Delay(200); // Give time for all connections to register
await Task.Delay(100); // Give time for all connections to register
// Act - Send message from first client
var testMessage = "Mass broadcast";