From 7355df979da64b1970bc0aefb9f207b5dec2fefe Mon Sep 17 00:00:00 2001
From: Jayaraman Venkatesan
<112980436+jayaraman-venkatesan@users.noreply.github.com>
Date: Thu, 2 Apr 2026 18:06:08 -0400
Subject: [PATCH 1/4] feat/1421 added feature to enable and disable mappings
---
.../Admin/Mappings/MappingModel.cs | 9 +-
src/WireMock.Net.Minimal/Mapping.cs | 3 +
.../Owin/MappingMatcher.cs | 1 +
.../Serialization/MappingConverter.cs | 1 +
.../Server/IRespondWithAProvider.cs | 7 +
.../Server/RespondWithAProvider.cs | 13 ++
.../Server/WireMockServer.Admin.cs | 49 +++++++
.../Server/WireMockServer.ConvertMapping.cs | 5 +
.../IWireMockAdminApi.cs | 16 +++
src/WireMock.Net.Shared/IMapping.cs | 10 +-
.../WireMockAdminApiTests.IsEnabled.cs | 134 ++++++++++++++++++
.../Owin/MappingMatcherTests.cs | 31 ++++
12 files changed, 276 insertions(+), 3 deletions(-)
create mode 100644 test/WireMock.Net.Tests/AdminApi/WireMockAdminApiTests.IsEnabled.cs
diff --git a/src/WireMock.Net.Abstractions/Admin/Mappings/MappingModel.cs b/src/WireMock.Net.Abstractions/Admin/Mappings/MappingModel.cs
index a6c7ce1c9..e07ba1564 100644
--- a/src/WireMock.Net.Abstractions/Admin/Mappings/MappingModel.cs
+++ b/src/WireMock.Net.Abstractions/Admin/Mappings/MappingModel.cs
@@ -55,12 +55,17 @@ public class MappingModel
/// In case the value is null state will not be changed.
///
public string? SetStateTo { get; set; }
-
+
///
/// The number of times this match should be matched before the state will be changed to the specified one.
///
public int? TimesInSameState { get; set; }
+ ///
+ /// Value to determing if the mapping is active
+ ///
+ public bool? IsEnabled { get; set; }
+
///
/// The request model.
///
@@ -100,7 +105,7 @@ public class MappingModel
///
public object? Data { get; set; }
- ///
+ ///
/// The probability when this request should be matched. Value is between 0 and 1. [Optional]
///
public double? Probability { get; set; }
diff --git a/src/WireMock.Net.Minimal/Mapping.cs b/src/WireMock.Net.Minimal/Mapping.cs
index 181c7fa99..967caefac 100644
--- a/src/WireMock.Net.Minimal/Mapping.cs
+++ b/src/WireMock.Net.Minimal/Mapping.cs
@@ -62,6 +62,9 @@ public class Mapping : IMapping
///
public bool IsProxy => Provider is ProxyAsyncResponseProvider;
+ ///
+ public bool IsEnabled { get; set; } = true;
+
///
public bool LogMapping => Provider is not (DynamicResponseProvider or DynamicAsyncResponseProvider);
diff --git a/src/WireMock.Net.Minimal/Owin/MappingMatcher.cs b/src/WireMock.Net.Minimal/Owin/MappingMatcher.cs
index 0750afce2..d024f243f 100644
--- a/src/WireMock.Net.Minimal/Owin/MappingMatcher.cs
+++ b/src/WireMock.Net.Minimal/Owin/MappingMatcher.cs
@@ -19,6 +19,7 @@ internal class MappingMatcher(IWireMockMiddlewareOptions options, IRandomizerDou
var possibleMappings = new List();
var mappings = _options.Mappings.Values
+ .Where(m=>m.IsEnabled)
.Where(m => m.TimeSettings.IsValid())
.Where(m => m.Probability is null || _randomizerDoubleBetween0And1.Generate() <= m.Probability)
.ToArray();
diff --git a/src/WireMock.Net.Minimal/Serialization/MappingConverter.cs b/src/WireMock.Net.Minimal/Serialization/MappingConverter.cs
index 90b3d246d..08ca727ad 100644
--- a/src/WireMock.Net.Minimal/Serialization/MappingConverter.cs
+++ b/src/WireMock.Net.Minimal/Serialization/MappingConverter.cs
@@ -275,6 +275,7 @@ public MappingModel ToMappingModel(IMapping mapping)
TimesInSameState = !string.IsNullOrWhiteSpace(mapping.NextState) ? mapping.TimesInSameState : null,
Data = mapping.Data,
Probability = mapping.Probability,
+ IsEnabled = mapping.IsEnabled ? null : false,
Request = new RequestModel
{
Headers = headerMatchers.Any() ? headerMatchers.Select(hm => new HeaderModel
diff --git a/src/WireMock.Net.Minimal/Server/IRespondWithAProvider.cs b/src/WireMock.Net.Minimal/Server/IRespondWithAProvider.cs
index a26e45240..d3578bf03 100644
--- a/src/WireMock.Net.Minimal/Server/IRespondWithAProvider.cs
+++ b/src/WireMock.Net.Minimal/Server/IRespondWithAProvider.cs
@@ -234,6 +234,13 @@ IRespondWithAProvider WithWebhook(
/// The .
IRespondWithAProvider WithProbability(double probability);
+ ///
+ /// Define whether this mapping is enabled. Defaults to true.
+ ///
+ /// Whether this mapping is enabled.
+ /// The .
+ IRespondWithAProvider WithIsEnabled(bool isEnabled);
+
///
/// Define a Grpc ProtoDefinition which is used for the request and the response.
/// This can be a ProtoDefinition as a string, or an id when the ProtoDefinitions are defined at the WireMockServer.
diff --git a/src/WireMock.Net.Minimal/Server/RespondWithAProvider.cs b/src/WireMock.Net.Minimal/Server/RespondWithAProvider.cs
index 65422f6cc..15a876237 100644
--- a/src/WireMock.Net.Minimal/Server/RespondWithAProvider.cs
+++ b/src/WireMock.Net.Minimal/Server/RespondWithAProvider.cs
@@ -37,6 +37,7 @@ internal class RespondWithAProvider : IRespondWithAProvider
private int _timesInSameState = 1;
private bool? _useWebhookFireAndForget;
private double? _probability;
+ private bool _isEnabled = true;
private GraphQLSchemaDetails? _graphQLSchemaDetails; // Future Use.
public Guid Guid { get; private set; }
@@ -108,6 +109,11 @@ public void RespondWith(IResponseProvider provider)
mapping.WithProbability(_probability.Value);
}
+ if (!_isEnabled)
+ {
+ mapping.IsEnabled = false;
+ }
+
if (ProtoDefinition != null)
{
mapping.WithProtoDefinition(ProtoDefinition.Value);
@@ -354,6 +360,13 @@ public IRespondWithAProvider WithProbability(double probability)
return this;
}
+ ///
+ public IRespondWithAProvider WithIsEnabled(bool isEnabled)
+ {
+ _isEnabled = isEnabled;
+ return this;
+ }
+
///
public IRespondWithAProvider WithProtoDefinition(params string[] protoDefinitionOrId)
{
diff --git a/src/WireMock.Net.Minimal/Server/WireMockServer.Admin.cs b/src/WireMock.Net.Minimal/Server/WireMockServer.Admin.cs
index 80e12db65..fc07a729f 100644
--- a/src/WireMock.Net.Minimal/Server/WireMockServer.Admin.cs
+++ b/src/WireMock.Net.Minimal/Server/WireMockServer.Admin.cs
@@ -57,6 +57,8 @@ public AdminPaths(WireMockServerSettings settings)
public string OpenApi => $"{_prefix}/openapi";
public RegexMatcher MappingsGuidPathMatcher => new($"^{_prefixEscaped}\\/mappings\\/([0-9A-Fa-f]{{8}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{12}})$");
+ public RegexMatcher MappingsGuidEnablePathMatcher => new($"^{_prefixEscaped}\\/mappings\\/([0-9A-Fa-f]{{8}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{12}})\\/enable$");
+ public RegexMatcher MappingsGuidDisablePathMatcher => new($"^{_prefixEscaped}\\/mappings\\/([0-9A-Fa-f]{{8}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{12}})\\/disable$");
public RegexMatcher MappingsCodeGuidPathMatcher => new($"^{_prefixEscaped}\\/mappings\\/code\\/([0-9A-Fa-f]{{8}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{12}})$");
public RegexMatcher RequestsGuidPathMatcher => new($"^{_prefixEscaped}\\/requests\\/([0-9A-Fa-f]{{8}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{4}}[-][0-9A-Fa-f]{{12}})$");
public RegexMatcher ScenariosNameMatcher => new($"^{_prefixEscaped}\\/scenarios\\/.+$");
@@ -100,6 +102,12 @@ private void InitAdmin()
Given(Request.Create().WithPath(_adminPaths.MappingsGuidPathMatcher).UsingPut().WithHeader(HttpKnownHeaderNames.ContentType, AdminRequestContentTypeJson)).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingPut));
Given(Request.Create().WithPath(_adminPaths.MappingsGuidPathMatcher).UsingDelete()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingDelete));
+ // __admin/mappings/{guid}/enable
+ Given(Request.Create().WithPath(_adminPaths.MappingsGuidEnablePathMatcher).UsingPut()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingEnable));
+
+ // __admin/mappings/{guid}/disable
+ Given(Request.Create().WithPath(_adminPaths.MappingsGuidDisablePathMatcher).UsingPut()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingDisable));
+
// __admin/mappings/code/{guid}
Given(Request.Create().WithPath(_adminPaths.MappingsCodeGuidPathMatcher).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingCodeGet));
@@ -426,6 +434,47 @@ private static bool TryParseGuidFromRequestMessage(IRequestMessage requestMessag
var lastPart = requestMessage.Path.Split('/').LastOrDefault();
return Guid.TryParse(lastPart, out guid);
}
+
+ private static bool TryParseGuidFromSecondToLastSegment(IRequestMessage requestMessage, out Guid guid)
+ {
+ var parts = requestMessage.Path.Split('/');
+ if (parts.Length >= 2 && Guid.TryParse(parts[parts.Length - 2], out guid))
+ return true;
+ guid = Guid.Empty;
+ return false;
+ }
+
+ private IResponseMessage MappingEnable(HttpContext _, IRequestMessage requestMessage)
+ {
+ if (TryParseGuidFromSecondToLastSegment(requestMessage, out var guid))
+ {
+ var mapping = Mappings.FirstOrDefault(m => !m.IsAdminInterface && m.Guid == guid);
+ if (mapping != null)
+ {
+ mapping.IsEnabled = true;
+ return ResponseMessageBuilder.Create(HttpStatusCode.OK, "Mapping enabled", guid);
+ }
+ }
+
+ _settings.Logger.Warn("HttpStatusCode set to 404 : Mapping not found");
+ return ResponseMessageBuilder.Create(HttpStatusCode.NotFound, "Mapping not found");
+ }
+
+ private IResponseMessage MappingDisable(HttpContext _, IRequestMessage requestMessage)
+ {
+ if (TryParseGuidFromSecondToLastSegment(requestMessage, out var guid))
+ {
+ var mapping = Mappings.FirstOrDefault(m => !m.IsAdminInterface && m.Guid == guid);
+ if (mapping != null)
+ {
+ mapping.IsEnabled = false;
+ return ResponseMessageBuilder.Create(HttpStatusCode.OK, "Mapping disabled", guid);
+ }
+ }
+
+ _settings.Logger.Warn("HttpStatusCode set to 404 : Mapping not found");
+ return ResponseMessageBuilder.Create(HttpStatusCode.NotFound, "Mapping not found");
+ }
#endregion Mapping/{guid}
#region Mappings
diff --git a/src/WireMock.Net.Minimal/Server/WireMockServer.ConvertMapping.cs b/src/WireMock.Net.Minimal/Server/WireMockServer.ConvertMapping.cs
index 4cff565dc..e95fdf30f 100644
--- a/src/WireMock.Net.Minimal/Server/WireMockServer.ConvertMapping.cs
+++ b/src/WireMock.Net.Minimal/Server/WireMockServer.ConvertMapping.cs
@@ -120,6 +120,11 @@ private Guid ConvertMappingAndRegisterAsRespondProvider(MappingModel mappingMode
respondProvider.WithProbability(mappingModel.Probability.Value);
}
+ if (mappingModel.IsEnabled == false)
+ {
+ respondProvider.WithIsEnabled(false);
+ }
+
// ProtoDefinition is defined at Mapping level
if (mappingModel.ProtoDefinition != null)
{
diff --git a/src/WireMock.Net.RestClient/IWireMockAdminApi.cs b/src/WireMock.Net.RestClient/IWireMockAdminApi.cs
index ee265c0fe..82fd1cb86 100644
--- a/src/WireMock.Net.RestClient/IWireMockAdminApi.cs
+++ b/src/WireMock.Net.RestClient/IWireMockAdminApi.cs
@@ -163,6 +163,22 @@ public interface IWireMockAdminApi
[Header("Content-Type", "application/json")]
Task PutMappingAsync([Path] Guid guid, [Body] MappingModel mapping, CancellationToken cancellationToken = default);
+ ///
+ /// Enable a mapping based on the guid.
+ ///
+ /// The Guid.
+ /// The optional cancellationToken.
+ [Put("mappings/{guid}/enable")]
+ Task EnableMappingAsync([Path] Guid guid, CancellationToken cancellationToken = default);
+
+ ///
+ /// Disable a mapping based on the guid.
+ ///
+ /// The Guid.
+ /// The optional cancellationToken.
+ [Put("mappings/{guid}/disable")]
+ Task DisableMappingAsync([Path] Guid guid, CancellationToken cancellationToken = default);
+
///
/// Delete a mapping based on the guid
///
diff --git a/src/WireMock.Net.Shared/IMapping.cs b/src/WireMock.Net.Shared/IMapping.cs
index 498638497..8de641f82 100644
--- a/src/WireMock.Net.Shared/IMapping.cs
+++ b/src/WireMock.Net.Shared/IMapping.cs
@@ -108,6 +108,14 @@ public interface IMapping
///
bool IsProxy { get; }
+ ///
+ /// Gets a value indicating whether this mapping is a Proxy Mapping.
+ ///
+ ///
+ /// true if this mapping is a Proxy Mapping; otherwise, false.
+ ///
+ bool IsEnabled { get; set; }
+
///
/// Gets a value indicating whether this mapping to be logged.
///
@@ -135,7 +143,7 @@ public interface IMapping
///
object? Data { get; }
- ///
+ ///
/// The probability when this request should be matched. Value is between 0 and 1. [Optional]
///
double? Probability { get; }
diff --git a/test/WireMock.Net.Tests/AdminApi/WireMockAdminApiTests.IsEnabled.cs b/test/WireMock.Net.Tests/AdminApi/WireMockAdminApiTests.IsEnabled.cs
new file mode 100644
index 000000000..c47d517ac
--- /dev/null
+++ b/test/WireMock.Net.Tests/AdminApi/WireMockAdminApiTests.IsEnabled.cs
@@ -0,0 +1,134 @@
+// Copyright © WireMock.Net
+
+using RestEase;
+using WireMock.Admin.Mappings;
+using WireMock.Client;
+using WireMock.Server;
+
+namespace WireMock.Net.Tests.AdminApi;
+
+public partial class WireMockAdminApiTests
+{
+ [Fact]
+ public async Task IWireMockAdminApi_PostMappingAsync_WithIsEnabledFalse_DoesNotMatchRequests()
+ {
+ // Arrange
+ var ct = TestContext.Current.CancellationToken;
+ using var server = WireMockServer.StartWithAdminInterface();
+ var api = RestClient.For(server.Urls[0]);
+ var httpClient = server.CreateClient();
+
+ var model = new MappingModel
+ {
+ Request = new RequestModel { Path = "/foo", Methods = ["GET"] },
+ Response = new ResponseModel { Body = "hello", StatusCode = 200 },
+ IsEnabled = false
+ };
+
+ // Act — POST the disabled mapping
+ var postResult = await api.PostMappingAsync(model, ct);
+ postResult.Should().NotBeNull();
+
+ // Assert — request should not be matched (404)
+ var response = await httpClient.GetAsync("/foo", ct);
+ ((int)response.StatusCode).Should().Be(404);
+
+ // Assert — mapping exists but IsEnabled is false
+ server.Mappings.Where(m => !m.IsAdminInterface).Should().ContainSingle(m => m.IsEnabled == false);
+ }
+
+ [Fact]
+ public async Task IWireMockAdminApi_DisableMappingAsync_PreventsMatching()
+ {
+ // Arrange
+ var ct = TestContext.Current.CancellationToken;
+ using var server = WireMockServer.StartWithAdminInterface();
+ var api = RestClient.For(server.Urls[0]);
+ var httpClient = server.CreateClient();
+
+ var model = new MappingModel
+ {
+ Request = new RequestModel { Path = "/bar", Methods = ["GET"] },
+ Response = new ResponseModel { Body = "world", StatusCode = 200 }
+ };
+ var postResult = await api.PostMappingAsync(model, ct);
+ var guid = postResult.Guid!.Value;
+
+ // Assert — mapping matches before disable
+ var before = await httpClient.GetAsync("/bar", ct);
+ ((int)before.StatusCode).Should().Be(200);
+
+ // Act — disable
+ var disableResult = await api.DisableMappingAsync(guid, ct);
+ disableResult.Status.Should().Be("Mapping disabled");
+
+ // Assert — no match after disable
+ var after = await httpClient.GetAsync("/bar", ct);
+ ((int)after.StatusCode).Should().Be(404);
+ }
+
+ [Fact]
+ public async Task IWireMockAdminApi_EnableMappingAsync_ResumesMatching()
+ {
+ // Arrange
+ var ct = TestContext.Current.CancellationToken;
+ using var server = WireMockServer.StartWithAdminInterface();
+ var api = RestClient.For(server.Urls[0]);
+ var httpClient = server.CreateClient();
+
+ var model = new MappingModel
+ {
+ Request = new RequestModel { Path = "/baz", Methods = ["GET"] },
+ Response = new ResponseModel { Body = "re-enabled", StatusCode = 200 },
+ IsEnabled = false
+ };
+ var postResult = await api.PostMappingAsync(model, ct);
+ var guid = postResult.Guid!.Value;
+
+ // Assert — no match while disabled
+ var before = await httpClient.GetAsync("/baz", ct);
+ ((int)before.StatusCode).Should().Be(404);
+
+ // Act — enable
+ var enableResult = await api.EnableMappingAsync(guid, ct);
+ enableResult.Status.Should().Be("Mapping enabled");
+
+ // Assert — mapping matches after enable
+ var after = await httpClient.GetAsync("/baz", ct);
+ ((int)after.StatusCode).Should().Be(200);
+ }
+
+ [Fact]
+ public async Task IWireMockAdminApi_GetMappingAsync_ReturnsIsEnabledFalse_WhenDisabled()
+ {
+ // Arrange
+ var ct = TestContext.Current.CancellationToken;
+ using var server = WireMockServer.StartWithAdminInterface();
+ var api = RestClient.For(server.Urls[0]);
+
+ var disabledModel = new MappingModel
+ {
+ Request = new RequestModel { Path = "/check-disabled" },
+ Response = new ResponseModel { Body = "x", StatusCode = 200 },
+ IsEnabled = false
+ };
+ var enabledModel = new MappingModel
+ {
+ Request = new RequestModel { Path = "/check-enabled" },
+ Response = new ResponseModel { Body = "y", StatusCode = 200 }
+ };
+
+ var disabledPost = await api.PostMappingAsync(disabledModel, ct);
+ var enabledPost = await api.PostMappingAsync(enabledModel, ct);
+
+ // Act
+ var disabledGot = await api.GetMappingAsync(disabledPost.Guid!.Value, ct);
+ var enabledGot = await api.GetMappingAsync(enabledPost.Guid!.Value, ct);
+
+ // Assert — disabled mapping serializes IsEnabled = false
+ disabledGot.IsEnabled.Should().BeFalse();
+
+ // Assert — enabled mapping omits IsEnabled (null = default true)
+ enabledGot.IsEnabled.Should().BeNull();
+ }
+}
diff --git a/test/WireMock.Net.Tests/Owin/MappingMatcherTests.cs b/test/WireMock.Net.Tests/Owin/MappingMatcherTests.cs
index 945d10d23..66d277488 100644
--- a/test/WireMock.Net.Tests/Owin/MappingMatcherTests.cs
+++ b/test/WireMock.Net.Tests/Owin/MappingMatcherTests.cs
@@ -56,6 +56,7 @@ public void MappingMatcher_FindBestMatch_WhenMappingThrowsException_ShouldReturn
{
// Assign
var mappingMock = new Mock();
+ mappingMock.SetupGet(m => m.IsEnabled).Returns(true);
mappingMock.Setup(m => m.GetRequestMatchResult(It.IsAny(), It.IsAny())).Throws();
var mappings = new ConcurrentDictionary();
@@ -229,6 +230,35 @@ public void MappingMatcher_FindBestMatch_WhenProbabilityDoesMatch_ShouldReturnPr
result.Match!.Mapping.Guid.Should().Be(withProbability);
}
+ [Fact]
+ public void MappingMatcher_FindBestMatch_WhenMappingIsDisabled_ShouldReturnNull()
+ {
+ // Assign
+ var guid = Guid.Parse("00000000-0000-0000-0000-000000000001");
+ var mappingMock = new Mock();
+ mappingMock.SetupGet(m => m.Guid).Returns(guid);
+ mappingMock.SetupGet(m => m.IsEnabled).Returns(false);
+ mappingMock.SetupGet(m => m.Probability).Returns((double?)null);
+
+ var matchResult = new RequestMatchResult();
+ matchResult.AddScore(typeof(object), 1.0, null);
+ mappingMock.Setup(m => m.GetRequestMatchResult(It.IsAny(), It.IsAny())).Returns(matchResult);
+
+ var mappings = new ConcurrentDictionary();
+ mappings.TryAdd(guid, mappingMock.Object);
+ _optionsMock.Setup(o => o.Mappings).Returns(mappings);
+
+ var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", "::1");
+
+ // Act
+ var result = _sut.FindBestMatch(request);
+
+ // Assert
+ result.Match.Should().BeNull();
+ result.Partial.Should().BeNull();
+ mappingMock.Verify(m => m.GetRequestMatchResult(It.IsAny(), It.IsAny()), Times.Never);
+ }
+
private static ConcurrentDictionary InitMappings(params (Guid guid, double[] scores, double? probability)[] matches)
{
var mappings = new ConcurrentDictionary();
@@ -237,6 +267,7 @@ private static ConcurrentDictionary InitMappings(params (Guid gu
{
var mappingMock = new Mock();
mappingMock.SetupGet(m => m.Guid).Returns(match.guid);
+ mappingMock.SetupGet(m => m.IsEnabled).Returns(true);
var requestMatchResult = new RequestMatchResult();
foreach (var score in match.scores)
From 39f3710b1143789fea9e7658dd8dbc8e8a9ad8fe Mon Sep 17 00:00:00 2001
From: Jayaraman Venkatesan
<112980436+jayaraman-venkatesan@users.noreply.github.com>
Date: Thu, 2 Apr 2026 18:24:12 -0400
Subject: [PATCH 2/4] feat/1421 updated test constants to reflect 2 new admin
endpoints /enable and /disable
---
test/WireMock.Net.Tests/Constants.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test/WireMock.Net.Tests/Constants.cs b/test/WireMock.Net.Tests/Constants.cs
index 30e4cdc7c..aadd6fb9e 100644
--- a/test/WireMock.Net.Tests/Constants.cs
+++ b/test/WireMock.Net.Tests/Constants.cs
@@ -6,5 +6,5 @@ internal static class Constants
{
internal const int NumStaticMappings = 10;
- internal const int NumAdminMappings = 37;
+ internal const int NumAdminMappings = 39;
}
\ No newline at end of file
From f179d787d1a89c2a67713db4b3c51be14040e617 Mon Sep 17 00:00:00 2001
From: Jayaraman Venkatesan
<112980436+jayaraman-venkatesan@users.noreply.github.com>
Date: Thu, 2 Apr 2026 22:04:17 -0400
Subject: [PATCH 3/4] feat/1421 updated tests to fix flakyness - removed delay
before assertion that is causing upstream connection from proxy to teardown
prematurely before test ends
---
.../WebSockets/WebSocketIntegrationTests.cs | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/test/WireMock.Net.Tests/WebSockets/WebSocketIntegrationTests.cs b/test/WireMock.Net.Tests/WebSockets/WebSocketIntegrationTests.cs
index 201e652ab..959d949f0 100644
--- a/test/WireMock.Net.Tests/WebSockets/WebSocketIntegrationTests.cs
+++ b/test/WireMock.Net.Tests/WebSockets/WebSocketIntegrationTests.cs
@@ -813,9 +813,9 @@ public async Task WithWebSocketProxy_Should_Proxy_Binary_Messages()
var receivedData = await client.ReceiveAsBytesAsync(cancellationToken: _ct);
- await Task.Delay(500, _ct);
-
- // Assert
+ // Assert immediately — receivedData is already in memory, no delay needed.
+ // Delaying here gives the proxy time to tear down its upstream connection, which
+ // causes the close handshake below to fail intermittently.
receivedData.Should().BeEquivalentTo(testData, "binary data should be proxied and echoed back");
await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "Test complete", _ct);
From f2eb41010e00d7c85728e3701f42a225f450ccbf Mon Sep 17 00:00:00 2001
From: Jayaraman Venkatesan
<112980436+jayaraman-venkatesan@users.noreply.github.com>
Date: Mon, 20 Apr 2026 21:09:03 -0400
Subject: [PATCH 4/4] feat/1421 addressing PR comments - Updated logic to
represent IsDisable insted of IsEnabled
---
.../Admin/Mappings/MappingModel.cs | 4 ++--
src/WireMock.Net.Minimal/Mapping.cs | 2 +-
.../Owin/MappingMatcher.cs | 2 +-
.../Serialization/MappingConverter.cs | 2 +-
.../Server/IRespondWithAProvider.cs | 6 ++---
.../Server/RespondWithAProvider.cs | 10 ++++-----
.../Server/WireMockServer.Admin.cs | 4 ++--
.../Server/WireMockServer.ConvertMapping.cs | 4 ++--
src/WireMock.Net.Shared/IMapping.cs | 6 ++---
...cs => WireMockAdminApiTests.IsDisabled.cs} | 22 +++++++++----------
.../Owin/MappingMatcherTests.cs | 6 ++---
.../WebSockets/WebSocketIntegrationTests.cs | 6 ++---
12 files changed, 37 insertions(+), 37 deletions(-)
rename test/WireMock.Net.Tests/AdminApi/{WireMockAdminApiTests.IsEnabled.cs => WireMockAdminApiTests.IsDisabled.cs} (88%)
diff --git a/src/WireMock.Net.Abstractions/Admin/Mappings/MappingModel.cs b/src/WireMock.Net.Abstractions/Admin/Mappings/MappingModel.cs
index e07ba1564..75c4bb957 100644
--- a/src/WireMock.Net.Abstractions/Admin/Mappings/MappingModel.cs
+++ b/src/WireMock.Net.Abstractions/Admin/Mappings/MappingModel.cs
@@ -62,9 +62,9 @@ public class MappingModel
public int? TimesInSameState { get; set; }
///
- /// Value to determing if the mapping is active
+ /// Value to determine if the mapping is disabled. Defaults to null (not disabled).
///
- public bool? IsEnabled { get; set; }
+ public bool? IsDisabled { get; set; }
///
/// The request model.
diff --git a/src/WireMock.Net.Minimal/Mapping.cs b/src/WireMock.Net.Minimal/Mapping.cs
index 967caefac..b4441ce99 100644
--- a/src/WireMock.Net.Minimal/Mapping.cs
+++ b/src/WireMock.Net.Minimal/Mapping.cs
@@ -63,7 +63,7 @@ public class Mapping : IMapping
public bool IsProxy => Provider is ProxyAsyncResponseProvider;
///
- public bool IsEnabled { get; set; } = true;
+ public bool IsDisabled { get; set; }
///
public bool LogMapping => Provider is not (DynamicResponseProvider or DynamicAsyncResponseProvider);
diff --git a/src/WireMock.Net.Minimal/Owin/MappingMatcher.cs b/src/WireMock.Net.Minimal/Owin/MappingMatcher.cs
index d024f243f..97d1fc58b 100644
--- a/src/WireMock.Net.Minimal/Owin/MappingMatcher.cs
+++ b/src/WireMock.Net.Minimal/Owin/MappingMatcher.cs
@@ -19,7 +19,7 @@ internal class MappingMatcher(IWireMockMiddlewareOptions options, IRandomizerDou
var possibleMappings = new List();
var mappings = _options.Mappings.Values
- .Where(m=>m.IsEnabled)
+ .Where(m => !m.IsDisabled)
.Where(m => m.TimeSettings.IsValid())
.Where(m => m.Probability is null || _randomizerDoubleBetween0And1.Generate() <= m.Probability)
.ToArray();
diff --git a/src/WireMock.Net.Minimal/Serialization/MappingConverter.cs b/src/WireMock.Net.Minimal/Serialization/MappingConverter.cs
index 08ca727ad..f26c4ddfb 100644
--- a/src/WireMock.Net.Minimal/Serialization/MappingConverter.cs
+++ b/src/WireMock.Net.Minimal/Serialization/MappingConverter.cs
@@ -275,7 +275,7 @@ public MappingModel ToMappingModel(IMapping mapping)
TimesInSameState = !string.IsNullOrWhiteSpace(mapping.NextState) ? mapping.TimesInSameState : null,
Data = mapping.Data,
Probability = mapping.Probability,
- IsEnabled = mapping.IsEnabled ? null : false,
+ IsDisabled = mapping.IsDisabled ? true : null,
Request = new RequestModel
{
Headers = headerMatchers.Any() ? headerMatchers.Select(hm => new HeaderModel
diff --git a/src/WireMock.Net.Minimal/Server/IRespondWithAProvider.cs b/src/WireMock.Net.Minimal/Server/IRespondWithAProvider.cs
index d3578bf03..c76696f3c 100644
--- a/src/WireMock.Net.Minimal/Server/IRespondWithAProvider.cs
+++ b/src/WireMock.Net.Minimal/Server/IRespondWithAProvider.cs
@@ -235,11 +235,11 @@ IRespondWithAProvider WithWebhook(
IRespondWithAProvider WithProbability(double probability);
///
- /// Define whether this mapping is enabled. Defaults to true.
+ /// Define whether this mapping is disabled. Defaults to false.
///
- /// Whether this mapping is enabled.
+ /// Whether this mapping is disabled.
/// The .
- IRespondWithAProvider WithIsEnabled(bool isEnabled);
+ IRespondWithAProvider WithIsDisabled(bool isDisabled);
///
/// Define a Grpc ProtoDefinition which is used for the request and the response.
diff --git a/src/WireMock.Net.Minimal/Server/RespondWithAProvider.cs b/src/WireMock.Net.Minimal/Server/RespondWithAProvider.cs
index 15a876237..499caf668 100644
--- a/src/WireMock.Net.Minimal/Server/RespondWithAProvider.cs
+++ b/src/WireMock.Net.Minimal/Server/RespondWithAProvider.cs
@@ -37,7 +37,7 @@ internal class RespondWithAProvider : IRespondWithAProvider
private int _timesInSameState = 1;
private bool? _useWebhookFireAndForget;
private double? _probability;
- private bool _isEnabled = true;
+ private bool _isDisabled = false;
private GraphQLSchemaDetails? _graphQLSchemaDetails; // Future Use.
public Guid Guid { get; private set; }
@@ -109,9 +109,9 @@ public void RespondWith(IResponseProvider provider)
mapping.WithProbability(_probability.Value);
}
- if (!_isEnabled)
+ if (_isDisabled)
{
- mapping.IsEnabled = false;
+ mapping.IsDisabled = true;
}
if (ProtoDefinition != null)
@@ -361,9 +361,9 @@ public IRespondWithAProvider WithProbability(double probability)
}
///
- public IRespondWithAProvider WithIsEnabled(bool isEnabled)
+ public IRespondWithAProvider WithIsDisabled(bool isDisabled)
{
- _isEnabled = isEnabled;
+ _isDisabled = isDisabled;
return this;
}
diff --git a/src/WireMock.Net.Minimal/Server/WireMockServer.Admin.cs b/src/WireMock.Net.Minimal/Server/WireMockServer.Admin.cs
index fc07a729f..48a4990c1 100644
--- a/src/WireMock.Net.Minimal/Server/WireMockServer.Admin.cs
+++ b/src/WireMock.Net.Minimal/Server/WireMockServer.Admin.cs
@@ -451,7 +451,7 @@ private IResponseMessage MappingEnable(HttpContext _, IRequestMessage requestMes
var mapping = Mappings.FirstOrDefault(m => !m.IsAdminInterface && m.Guid == guid);
if (mapping != null)
{
- mapping.IsEnabled = true;
+ mapping.IsDisabled = false;
return ResponseMessageBuilder.Create(HttpStatusCode.OK, "Mapping enabled", guid);
}
}
@@ -467,7 +467,7 @@ private IResponseMessage MappingDisable(HttpContext _, IRequestMessage requestMe
var mapping = Mappings.FirstOrDefault(m => !m.IsAdminInterface && m.Guid == guid);
if (mapping != null)
{
- mapping.IsEnabled = false;
+ mapping.IsDisabled = true;
return ResponseMessageBuilder.Create(HttpStatusCode.OK, "Mapping disabled", guid);
}
}
diff --git a/src/WireMock.Net.Minimal/Server/WireMockServer.ConvertMapping.cs b/src/WireMock.Net.Minimal/Server/WireMockServer.ConvertMapping.cs
index e95fdf30f..fa27c1a2e 100644
--- a/src/WireMock.Net.Minimal/Server/WireMockServer.ConvertMapping.cs
+++ b/src/WireMock.Net.Minimal/Server/WireMockServer.ConvertMapping.cs
@@ -120,9 +120,9 @@ private Guid ConvertMappingAndRegisterAsRespondProvider(MappingModel mappingMode
respondProvider.WithProbability(mappingModel.Probability.Value);
}
- if (mappingModel.IsEnabled == false)
+ if (mappingModel.IsDisabled == true)
{
- respondProvider.WithIsEnabled(false);
+ respondProvider.WithIsDisabled(true);
}
// ProtoDefinition is defined at Mapping level
diff --git a/src/WireMock.Net.Shared/IMapping.cs b/src/WireMock.Net.Shared/IMapping.cs
index 8de641f82..ee2f3dacf 100644
--- a/src/WireMock.Net.Shared/IMapping.cs
+++ b/src/WireMock.Net.Shared/IMapping.cs
@@ -109,12 +109,12 @@ public interface IMapping
bool IsProxy { get; }
///
- /// Gets a value indicating whether this mapping is a Proxy Mapping.
+ /// Gets a value indicating whether this mapping is disabled.
///
///
- /// true if this mapping is a Proxy Mapping; otherwise, false.
+ /// true if this mapping is disabled; otherwise, false.
///
- bool IsEnabled { get; set; }
+ bool IsDisabled { get; set; }
///
/// Gets a value indicating whether this mapping to be logged.
diff --git a/test/WireMock.Net.Tests/AdminApi/WireMockAdminApiTests.IsEnabled.cs b/test/WireMock.Net.Tests/AdminApi/WireMockAdminApiTests.IsDisabled.cs
similarity index 88%
rename from test/WireMock.Net.Tests/AdminApi/WireMockAdminApiTests.IsEnabled.cs
rename to test/WireMock.Net.Tests/AdminApi/WireMockAdminApiTests.IsDisabled.cs
index c47d517ac..4d247d303 100644
--- a/test/WireMock.Net.Tests/AdminApi/WireMockAdminApiTests.IsEnabled.cs
+++ b/test/WireMock.Net.Tests/AdminApi/WireMockAdminApiTests.IsDisabled.cs
@@ -10,7 +10,7 @@ namespace WireMock.Net.Tests.AdminApi;
public partial class WireMockAdminApiTests
{
[Fact]
- public async Task IWireMockAdminApi_PostMappingAsync_WithIsEnabledFalse_DoesNotMatchRequests()
+ public async Task IWireMockAdminApi_PostMappingAsync_WithIsDisabledTrue_DoesNotMatchRequests()
{
// Arrange
var ct = TestContext.Current.CancellationToken;
@@ -22,7 +22,7 @@ public async Task IWireMockAdminApi_PostMappingAsync_WithIsEnabledFalse_DoesNotM
{
Request = new RequestModel { Path = "/foo", Methods = ["GET"] },
Response = new ResponseModel { Body = "hello", StatusCode = 200 },
- IsEnabled = false
+ IsDisabled = true
};
// Act — POST the disabled mapping
@@ -33,8 +33,8 @@ public async Task IWireMockAdminApi_PostMappingAsync_WithIsEnabledFalse_DoesNotM
var response = await httpClient.GetAsync("/foo", ct);
((int)response.StatusCode).Should().Be(404);
- // Assert — mapping exists but IsEnabled is false
- server.Mappings.Where(m => !m.IsAdminInterface).Should().ContainSingle(m => m.IsEnabled == false);
+ // Assert — mapping exists but IsDisabled is true
+ server.Mappings.Where(m => !m.IsAdminInterface).Should().ContainSingle(m => m.IsDisabled == true);
}
[Fact]
@@ -80,7 +80,7 @@ public async Task IWireMockAdminApi_EnableMappingAsync_ResumesMatching()
{
Request = new RequestModel { Path = "/baz", Methods = ["GET"] },
Response = new ResponseModel { Body = "re-enabled", StatusCode = 200 },
- IsEnabled = false
+ IsDisabled = true
};
var postResult = await api.PostMappingAsync(model, ct);
var guid = postResult.Guid!.Value;
@@ -99,7 +99,7 @@ public async Task IWireMockAdminApi_EnableMappingAsync_ResumesMatching()
}
[Fact]
- public async Task IWireMockAdminApi_GetMappingAsync_ReturnsIsEnabledFalse_WhenDisabled()
+ public async Task IWireMockAdminApi_GetMappingAsync_ReturnsIsDisabledTrue_WhenDisabled()
{
// Arrange
var ct = TestContext.Current.CancellationToken;
@@ -110,7 +110,7 @@ public async Task IWireMockAdminApi_GetMappingAsync_ReturnsIsEnabledFalse_WhenDi
{
Request = new RequestModel { Path = "/check-disabled" },
Response = new ResponseModel { Body = "x", StatusCode = 200 },
- IsEnabled = false
+ IsDisabled = true
};
var enabledModel = new MappingModel
{
@@ -125,10 +125,10 @@ public async Task IWireMockAdminApi_GetMappingAsync_ReturnsIsEnabledFalse_WhenDi
var disabledGot = await api.GetMappingAsync(disabledPost.Guid!.Value, ct);
var enabledGot = await api.GetMappingAsync(enabledPost.Guid!.Value, ct);
- // Assert — disabled mapping serializes IsEnabled = false
- disabledGot.IsEnabled.Should().BeFalse();
+ // Assert — disabled mapping serializes IsDisabled = true
+ disabledGot.IsDisabled.Should().BeTrue();
- // Assert — enabled mapping omits IsEnabled (null = default true)
- enabledGot.IsEnabled.Should().BeNull();
+ // Assert — enabled mapping omits IsDisabled (null = default not disabled)
+ enabledGot.IsDisabled.Should().BeNull();
}
}
diff --git a/test/WireMock.Net.Tests/Owin/MappingMatcherTests.cs b/test/WireMock.Net.Tests/Owin/MappingMatcherTests.cs
index 66d277488..17da566c3 100644
--- a/test/WireMock.Net.Tests/Owin/MappingMatcherTests.cs
+++ b/test/WireMock.Net.Tests/Owin/MappingMatcherTests.cs
@@ -56,7 +56,7 @@ public void MappingMatcher_FindBestMatch_WhenMappingThrowsException_ShouldReturn
{
// Assign
var mappingMock = new Mock();
- mappingMock.SetupGet(m => m.IsEnabled).Returns(true);
+ mappingMock.SetupGet(m => m.IsDisabled).Returns(false);
mappingMock.Setup(m => m.GetRequestMatchResult(It.IsAny(), It.IsAny())).Throws();
var mappings = new ConcurrentDictionary();
@@ -237,7 +237,7 @@ public void MappingMatcher_FindBestMatch_WhenMappingIsDisabled_ShouldReturnNull(
var guid = Guid.Parse("00000000-0000-0000-0000-000000000001");
var mappingMock = new Mock();
mappingMock.SetupGet(m => m.Guid).Returns(guid);
- mappingMock.SetupGet(m => m.IsEnabled).Returns(false);
+ mappingMock.SetupGet(m => m.IsDisabled).Returns(true);
mappingMock.SetupGet(m => m.Probability).Returns((double?)null);
var matchResult = new RequestMatchResult();
@@ -267,7 +267,7 @@ private static ConcurrentDictionary InitMappings(params (Guid gu
{
var mappingMock = new Mock();
mappingMock.SetupGet(m => m.Guid).Returns(match.guid);
- mappingMock.SetupGet(m => m.IsEnabled).Returns(true);
+ mappingMock.SetupGet(m => m.IsDisabled).Returns(false);
var requestMatchResult = new RequestMatchResult();
foreach (var score in match.scores)
diff --git a/test/WireMock.Net.Tests/WebSockets/WebSocketIntegrationTests.cs b/test/WireMock.Net.Tests/WebSockets/WebSocketIntegrationTests.cs
index 959d949f0..201e652ab 100644
--- a/test/WireMock.Net.Tests/WebSockets/WebSocketIntegrationTests.cs
+++ b/test/WireMock.Net.Tests/WebSockets/WebSocketIntegrationTests.cs
@@ -813,9 +813,9 @@ public async Task WithWebSocketProxy_Should_Proxy_Binary_Messages()
var receivedData = await client.ReceiveAsBytesAsync(cancellationToken: _ct);
- // Assert immediately — receivedData is already in memory, no delay needed.
- // Delaying here gives the proxy time to tear down its upstream connection, which
- // causes the close handshake below to fail intermittently.
+ await Task.Delay(500, _ct);
+
+ // Assert
receivedData.Should().BeEquivalentTo(testData, "binary data should be proxied and echoed back");
await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "Test complete", _ct);