From 1ab6ca5531518bb04f97fc59a3eef09b3e189097 Mon Sep 17 00:00:00 2001 From: Peter Benko Date: Mon, 6 Jul 2026 18:27:11 +0200 Subject: [PATCH 1/2] Fix: Fully respect configured custom admin path --- .../Owin/AspNetCoreSelfHost.cs | 1 + src/WireMock.Net.Minimal/Owin/IAdminPaths.cs | 28 +++++++++++ .../Owin/IWireMockMiddlewareOptions.cs | 2 + .../Owin/WireMockMiddleware.cs | 3 +- .../Owin/WireMockMiddlewareLogger.cs | 47 ++++++++++--------- .../Owin/WireMockMiddlewareOptions.cs | 3 ++ .../Owin/WireMockMiddlewareOptionsHelper.cs | 1 + src/WireMock.Net.Minimal/Server/AdminPaths.cs | 40 ++++++++++++++++ .../Server/WireMockServer.Admin.cs | 47 +++---------------- .../Owin/WireMockMiddlewareTests.cs | 4 ++ 10 files changed, 113 insertions(+), 63 deletions(-) create mode 100644 src/WireMock.Net.Minimal/Owin/IAdminPaths.cs create mode 100644 src/WireMock.Net.Minimal/Server/AdminPaths.cs diff --git a/src/WireMock.Net.Minimal/Owin/AspNetCoreSelfHost.cs b/src/WireMock.Net.Minimal/Owin/AspNetCoreSelfHost.cs index 35c183dc5..ab279d21a 100644 --- a/src/WireMock.Net.Minimal/Owin/AspNetCoreSelfHost.cs +++ b/src/WireMock.Net.Minimal/Owin/AspNetCoreSelfHost.cs @@ -66,6 +66,7 @@ public Task StartAsync() .ConfigureServices(services => { services.AddSingleton(_wireMockMiddlewareOptions); + services.AddSingleton(_wireMockMiddlewareOptions.AdminPaths); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); diff --git a/src/WireMock.Net.Minimal/Owin/IAdminPaths.cs b/src/WireMock.Net.Minimal/Owin/IAdminPaths.cs new file mode 100644 index 000000000..67c9f1869 --- /dev/null +++ b/src/WireMock.Net.Minimal/Owin/IAdminPaths.cs @@ -0,0 +1,28 @@ +using WireMock.Matchers; + +namespace WireMock.Owin; + +internal interface IAdminPaths +{ + string Files { get; } + string Health { get; } + string Mappings { get; } + string MappingsCode { get; } + string MappingsWireMockOrg { get; } + RegexMatcher MappingsGuidPathMatcher { get; } + RegexMatcher MappingsGuidEnablePathMatcher { get; } + RegexMatcher MappingsGuidDisablePathMatcher { get; } + RegexMatcher MappingsCodeGuidPathMatcher { get; } + RegexMatcher RequestsGuidPathMatcher { get; } + RegexMatcher ScenariosNameMatcher { get; } + RegexMatcher ScenariosNameWithStateMatcher { get; } + RegexMatcher ScenariosNameWithResetMatcher { get; } + RegexMatcher FilesFilenamePathMatcher { get; } + RegexMatcher ProtoDefinitionsIdPathMatcher { get; } + string Requests { get; } + string Settings { get; } + string Scenarios { get; } + string OpenApi { get; } + string ProtoDefinitions { get; } + bool Includes(string? path); +} \ No newline at end of file diff --git a/src/WireMock.Net.Minimal/Owin/IWireMockMiddlewareOptions.cs b/src/WireMock.Net.Minimal/Owin/IWireMockMiddlewareOptions.cs index 9bbf47fec..6f36c88b9 100644 --- a/src/WireMock.Net.Minimal/Owin/IWireMockMiddlewareOptions.cs +++ b/src/WireMock.Net.Minimal/Owin/IWireMockMiddlewareOptions.cs @@ -108,4 +108,6 @@ internal interface IWireMockMiddlewareOptions /// Set this property to customize how objects are serialized to and deserialized from JSON during mapping. /// IJsonConverter DefaultJsonSerializer { get; set; } + + IAdminPaths AdminPaths { get; set; } } \ No newline at end of file diff --git a/src/WireMock.Net.Minimal/Owin/WireMockMiddleware.cs b/src/WireMock.Net.Minimal/Owin/WireMockMiddleware.cs index 9cfa3e4ed..bd193e11e 100644 --- a/src/WireMock.Net.Minimal/Owin/WireMockMiddleware.cs +++ b/src/WireMock.Net.Minimal/Owin/WireMockMiddleware.cs @@ -21,6 +21,7 @@ internal class WireMockMiddleware( RequestDelegate next, #pragma warning restore CS9113 // Parameter is unread. IWireMockMiddlewareOptions options, + IAdminPaths adminPaths, IOwinRequestMapper requestMapper, IOwinResponseMapper responseMapper, IMappingMatcher mappingMatcher, @@ -64,7 +65,7 @@ private async Task InvokeInternalAsync(HttpContext ctx) Activity? activity = null; // Check if we should trace this request (optionally exclude admin requests) - var shouldTrace = tracingEnabled && !(excludeAdmin && request.Path.StartsWith("/__admin/")); + var shouldTrace = tracingEnabled && !(excludeAdmin && adminPaths.Includes(request.Path)); if (shouldTrace) { diff --git a/src/WireMock.Net.Minimal/Owin/WireMockMiddlewareLogger.cs b/src/WireMock.Net.Minimal/Owin/WireMockMiddlewareLogger.cs index 3a0d0a395..54f242ffb 100644 --- a/src/WireMock.Net.Minimal/Owin/WireMockMiddlewareLogger.cs +++ b/src/WireMock.Net.Minimal/Owin/WireMockMiddlewareLogger.cs @@ -9,39 +9,43 @@ namespace WireMock.Owin; internal class WireMockMiddlewareLogger( - IWireMockMiddlewareOptions _options, - LogEntryMapper _logEntryMapper, - IGuidUtils _guidUtils + IWireMockMiddlewareOptions options, + LogEntryMapper logEntryMapper, + IGuidUtils guidUtils, + IAdminPaths adminPaths ) : IWireMockMiddlewareLogger { public void LogRequestAndResponse(bool logRequest, RequestMessage request, IResponseMessage? response, MappingMatcherResult? match, MappingMatcherResult? partialMatch, Activity? activity) { + var mapping = match?.Mapping; + var partialMapping = partialMatch?.Mapping; + var logEntry = new LogEntry { - Guid = _guidUtils.NewGuid(), + Guid = guidUtils.NewGuid(), RequestMessage = request, ResponseMessage = response, - MappingGuid = match?.Mapping?.Guid, - MappingTitle = match?.Mapping?.Title, + MappingGuid = mapping?.Guid, + MappingTitle = mapping?.Title, RequestMatchResult = match?.RequestMatchResult, - PartialMappingGuid = partialMatch?.Mapping?.Guid, - PartialMappingTitle = partialMatch?.Mapping?.Title, + PartialMappingGuid = partialMapping?.Guid, + PartialMappingTitle = partialMapping?.Title, PartialMatchResult = partialMatch?.RequestMatchResult }; - WireMockActivitySource.EnrichWithLogEntry(activity, logEntry, _options.ActivityTracingOptions); + WireMockActivitySource.EnrichWithLogEntry(activity, logEntry, options.ActivityTracingOptions); activity?.Dispose(); LogLogEntry(logEntry, logRequest); try { - if (_options.SaveUnmatchedRequests == true && match?.RequestMatchResult is not { IsPerfectMatch: true }) + if (options.SaveUnmatchedRequests == true && match?.RequestMatchResult is not { IsPerfectMatch: true }) { var filename = $"{logEntry.Guid}.LogEntry.json"; - _options.FileSystemHandler?.WriteUnmatchedRequest(filename, _options.DefaultJsonSerializer.Serialize(logEntry)); + options.FileSystemHandler?.WriteUnmatchedRequest(filename, options.DefaultJsonSerializer.Serialize(logEntry)); } } catch @@ -52,34 +56,35 @@ public void LogRequestAndResponse(bool logRequest, RequestMessage request, IResp public void LogLogEntry(LogEntry entry, bool addRequest) { - _options.Logger.DebugRequestResponse(_logEntryMapper.Map(entry), entry.RequestMessage?.Path.StartsWith("/__admin/") == true); + var isAdminRequest = adminPaths.Includes(entry.RequestMessage?.Path); + options.Logger.DebugRequestResponse(logEntryMapper.Map(entry), isAdminRequest); // If addRequest is set to true and MaxRequestLogCount is null or does have a value greater than 0, try to add a new request log. - if (addRequest && _options.MaxRequestLogCount is null or > 0) + if (addRequest && options.MaxRequestLogCount is null or > 0) { TryAddLogEntry(entry); } // In case MaxRequestLogCount has a value greater than 0, try to delete existing request logs based on the count. - if (_options.MaxRequestLogCount is > 0) + if (options.MaxRequestLogCount is > 0) { - var logEntries = _options.LogEntries.ToList(); + var logEntries = options.LogEntries.ToList(); foreach (var logEntry in logEntries .OrderBy(le => le.RequestMessage?.DateTime ?? le.ResponseMessage?.DateTime) - .Take(logEntries.Count - _options.MaxRequestLogCount.Value)) + .Take(logEntries.Count - options.MaxRequestLogCount.Value)) { TryRemoveLogEntry(logEntry); } } // In case RequestLogExpirationDuration has a value greater than 0, try to delete existing request logs based on the date. - if (_options.RequestLogExpirationDuration is > 0) + if (options.RequestLogExpirationDuration is > 0) { - var logEntries = _options.LogEntries.ToList(); + var logEntries = options.LogEntries.ToList(); - var checkTime = DateTime.UtcNow.AddHours(-_options.RequestLogExpirationDuration.Value); + var checkTime = DateTime.UtcNow.AddHours(-options.RequestLogExpirationDuration.Value); foreach (var logEntry in logEntries.Where(le => le.RequestMessage?.DateTime < checkTime || le.ResponseMessage?.DateTime < checkTime)) { TryRemoveLogEntry(logEntry); @@ -91,7 +96,7 @@ private void TryAddLogEntry(LogEntry logEntry) { try { - _options.LogEntries.Add(logEntry); + options.LogEntries.Add(logEntry); } catch { @@ -103,7 +108,7 @@ private void TryRemoveLogEntry(LogEntry logEntry) { try { - _options.LogEntries.Remove(logEntry); + options.LogEntries.Remove(logEntry); } catch { diff --git a/src/WireMock.Net.Minimal/Owin/WireMockMiddlewareOptions.cs b/src/WireMock.Net.Minimal/Owin/WireMockMiddlewareOptions.cs index 50c97a762..87028f1ce 100644 --- a/src/WireMock.Net.Minimal/Owin/WireMockMiddlewareOptions.cs +++ b/src/WireMock.Net.Minimal/Owin/WireMockMiddlewareOptions.cs @@ -115,4 +115,7 @@ internal class WireMockMiddlewareOptions : IWireMockMiddlewareOptions /// public IJsonConverter DefaultJsonSerializer { get; set; } = new NewtonsoftJsonConverter(); + + /// + public IAdminPaths AdminPaths { get; set; } = new AdminPaths(null); } \ No newline at end of file diff --git a/src/WireMock.Net.Minimal/Owin/WireMockMiddlewareOptionsHelper.cs b/src/WireMock.Net.Minimal/Owin/WireMockMiddlewareOptionsHelper.cs index 2426693f8..fcbb97564 100644 --- a/src/WireMock.Net.Minimal/Owin/WireMockMiddlewareOptionsHelper.cs +++ b/src/WireMock.Net.Minimal/Owin/WireMockMiddlewareOptionsHelper.cs @@ -34,6 +34,7 @@ public static IWireMockMiddlewareOptions InitFromSettings( options.QueryParameterMultipleValueSupport = settings.QueryParameterMultipleValueSupport; options.RequestLogExpirationDuration = settings.RequestLogExpirationDuration; options.SaveUnmatchedRequests = settings.SaveUnmatchedRequests; + options.AdminPaths = new AdminPaths(settings.AdminPath); #if USE_ASPNETCORE options.AdditionalServiceRegistration = settings.AdditionalServiceRegistration; diff --git a/src/WireMock.Net.Minimal/Server/AdminPaths.cs b/src/WireMock.Net.Minimal/Server/AdminPaths.cs new file mode 100644 index 000000000..8f969e6ac --- /dev/null +++ b/src/WireMock.Net.Minimal/Server/AdminPaths.cs @@ -0,0 +1,40 @@ +// Copyright © WireMock.Net + +using System.Text.RegularExpressions; +using WireMock.Matchers; + +namespace WireMock.Owin; + +internal sealed class AdminPaths(string? adminPath) : IAdminPaths +{ + private const string DefaultAdminPathPrefix = "/__admin"; + + private readonly string _prefix = adminPath ?? DefaultAdminPathPrefix; + + public string Files => $"{_prefix}/files"; + public string Health => $"{_prefix}/health"; + public string Mappings => $"{_prefix}/mappings"; + public string MappingsCode => $"{_prefix}/mappings/code"; + public string MappingsWireMockOrg => $"{_prefix}mappings/wiremock.org"; + public string Requests => $"{_prefix}/requests"; + public string Settings => $"{_prefix}/settings"; + public string Scenarios => $"{_prefix}/scenarios"; + public string OpenApi => $"{_prefix}/openapi"; + public string ProtoDefinitions => $"{_prefix}/protodefinitions"; + + private string PrefixRegexEscaped => Regex.Escape(_prefix); + public RegexMatcher MappingsGuidPathMatcher => new($@"^{PrefixRegexEscaped}\/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($@"^{PrefixRegexEscaped}\/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($@"^{PrefixRegexEscaped}\/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($@"^{PrefixRegexEscaped}\/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($@"^{PrefixRegexEscaped}\/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($@"^{PrefixRegexEscaped}\/scenarios\/.+$"); + public RegexMatcher ScenariosNameWithStateMatcher => new($@"^{PrefixRegexEscaped}\/scenarios\/.+\/state$"); + public RegexMatcher ScenariosNameWithResetMatcher => new($@"^{PrefixRegexEscaped}\/scenarios\/.+\/reset$"); + public RegexMatcher FilesFilenamePathMatcher => new($@"^{PrefixRegexEscaped}\/files\/.+$"); + public RegexMatcher ProtoDefinitionsIdPathMatcher => new($@"^{PrefixRegexEscaped}\/protodefinitions\/.+$"); + + public bool Includes(string? path) => !string.IsNullOrEmpty(path) && path.StartsWith($"{_prefix}/"); + + public override string ToString() => _prefix; +} diff --git a/src/WireMock.Net.Minimal/Server/WireMockServer.Admin.cs b/src/WireMock.Net.Minimal/Server/WireMockServer.Admin.cs index 2a2e4e7d2..6190dc643 100644 --- a/src/WireMock.Net.Minimal/Server/WireMockServer.Admin.cs +++ b/src/WireMock.Net.Minimal/Server/WireMockServer.Admin.cs @@ -19,7 +19,6 @@ using WireMock.RequestBuilders; using WireMock.ResponseProviders; using WireMock.Serialization; -using WireMock.Settings; using WireMock.Types; using WireMock.Util; @@ -28,50 +27,16 @@ namespace WireMock.Server; public partial class WireMockServer { private const int EnhancedFileSystemWatcherTimeoutMs = 1000; - private const string DefaultAdminPathPrefix = "/__admin"; private const string QueryParamReloadStaticMappings = "reloadStaticMappings"; private static readonly Guid ProxyMappingGuid = new("e59914fd-782e-428e-91c1-4810ffb86567"); private static readonly RegexMatcher AdminRequestContentTypeJson = new ContentTypeMatcher(WireMockConstants.ContentTypeJson, true); private EnhancedFileSystemWatcher? _enhancedFileSystemWatcher; - private AdminPaths? _adminPaths; - - private sealed class AdminPaths - { - private readonly string _prefix; - private readonly string _prefixEscaped; - - public AdminPaths(WireMockServerSettings settings) - { - _prefix = settings.AdminPath ?? DefaultAdminPathPrefix; - _prefixEscaped = _prefix.Replace("/", "\\/"); - } - - public string Files => $"{_prefix}/files"; - public string Health => $"{_prefix}/health"; - public string Mappings => $"{_prefix}/mappings"; - public string MappingsCode => $"{_prefix}/mappings/code"; - public string MappingsWireMockOrg => $"{_prefix}mappings/wiremock.org"; - public string Requests => $"{_prefix}/requests"; - public string Settings => $"{_prefix}/settings"; - public string Scenarios => $"{_prefix}/scenarios"; - 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\\/.+$"); - public RegexMatcher ScenariosNameWithStateMatcher => new($"^{_prefixEscaped}\\/scenarios\\/.+\\/state$"); - public RegexMatcher ScenariosNameWithResetMatcher => new($"^{_prefixEscaped}\\/scenarios\\/.+\\/reset$"); - public RegexMatcher FilesFilenamePathMatcher => new($"^{_prefixEscaped}\\/files\\/.+$"); - public RegexMatcher ProtoDefinitionsIdPathMatcher => new($"^{_prefixEscaped}\\/protodefinitions\\/.+$"); - } + private IAdminPaths? _adminPaths; #region InitAdmin private void InitAdmin() { - _adminPaths = new AdminPaths(_settings); + _adminPaths = _options.AdminPaths; // __admin/health Given(Request.Create().WithPath(_adminPaths.Health).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(HealthGet)); @@ -631,7 +596,7 @@ private IResponseMessage RequestGet(HttpContext _, IRequestMessage requestMessag { if (TryParseGuidFromRequestMessage(requestMessage, out var guid)) { - var entry = LogEntries.SingleOrDefault(r => r.RequestMessage != null && !r.RequestMessage.Path.StartsWith("/__admin/") && r.Guid == guid); + var entry = LogEntries.SingleOrDefault(r => r.RequestMessage != null && !_adminPaths!.Includes(r.RequestMessage.Path) && r.Guid == guid); if (entry is { }) { var model = new LogEntryMapper(_options).Map(entry); @@ -660,7 +625,7 @@ private IResponseMessage RequestsGet(HttpContext _, IRequestMessage requestMessa { var logEntryMapper = new LogEntryMapper(_options); var result = LogEntries - .Where(r => r.RequestMessage != null && !r.RequestMessage.Path.StartsWith("/__admin/")) + .Where(r => r.RequestMessage != null && !_adminPaths!.Includes(r.RequestMessage.Path)) .Select(logEntryMapper.Map); return ToJson(result); @@ -682,7 +647,7 @@ private IResponseMessage RequestsFind(HttpContext _, IRequestMessage requestMess var request = (Request)InitRequestBuilder(requestModel); var dict = new Dictionary(); - foreach (var logEntry in LogEntries.Where(le => le.RequestMessage != null && !le.RequestMessage.Path.StartsWith("/__admin/"))) + foreach (var logEntry in LogEntries.Where(le => le.RequestMessage != null && !_adminPaths!.Includes(le.RequestMessage.Path))) { var requestMatchResult = new RequestMatchResult(); if (request.GetMatchingScore(logEntry.RequestMessage!, requestMatchResult) > MatchScores.AlmostPerfect) @@ -704,7 +669,7 @@ private IResponseMessage RequestsFindByMappingGuid(HttpContext _, IRequestMessag Guid.TryParse(value.ToString(), out var mappingGuid) ) { - var logEntries = LogEntries.Where(le => le.RequestMessage != null && !le.RequestMessage.Path.StartsWith("/__admin/") && le.MappingGuid == mappingGuid); + var logEntries = LogEntries.Where(le => le.RequestMessage != null && !_adminPaths!.Includes(le.RequestMessage.Path) && le.MappingGuid == mappingGuid); var logEntryMapper = new LogEntryMapper(_options); var result = logEntries.Select(logEntryMapper.Map); return ToJson(result); diff --git a/test/WireMock.Net.Tests/Owin/WireMockMiddlewareTests.cs b/test/WireMock.Net.Tests/Owin/WireMockMiddlewareTests.cs index 670328817..fd149196f 100644 --- a/test/WireMock.Net.Tests/Owin/WireMockMiddlewareTests.cs +++ b/test/WireMock.Net.Tests/Owin/WireMockMiddlewareTests.cs @@ -50,6 +50,9 @@ public WireMockMiddlewareTests() _guidUtilsMock = new Mock(); _guidUtilsMock.Setup(g => g.NewGuid()).Returns(NewGuid); + var adminPathsMock = new Mock(); + adminPathsMock.Setup(a => a.Includes(It.IsAny())).Returns((string? path) => path?.StartsWith("/__admin/") == true); + _dateTimeUtilsMock = new Mock(); _dateTimeUtilsMock.Setup(d => d.UtcNow).Returns(UtcNow); @@ -89,6 +92,7 @@ public WireMockMiddlewareTests() _sut = new WireMockMiddleware( _ => Task.CompletedTask, _optionsMock.Object, + adminPathsMock.Object, _requestMapperMock.Object, _responseMapperMock.Object, _matcherMock.Object, From 0933a120bcfeb72e86a5f900cc209bc708e6aaf5 Mon Sep 17 00:00:00 2001 From: Peter Benko Date: Mon, 6 Jul 2026 18:27:21 +0200 Subject: [PATCH 2/2] Add tests for custom admin path behaviour --- src/WireMock.Net.Minimal/Server/AdminPaths.cs | 2 +- .../WireMockServer.Admin.cs | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/WireMock.Net.Minimal/Server/AdminPaths.cs b/src/WireMock.Net.Minimal/Server/AdminPaths.cs index 8f969e6ac..60f108b74 100644 --- a/src/WireMock.Net.Minimal/Server/AdminPaths.cs +++ b/src/WireMock.Net.Minimal/Server/AdminPaths.cs @@ -8,7 +8,7 @@ namespace WireMock.Owin; internal sealed class AdminPaths(string? adminPath) : IAdminPaths { private const string DefaultAdminPathPrefix = "/__admin"; - + private readonly string _prefix = adminPath ?? DefaultAdminPathPrefix; public string Files => $"{_prefix}/files"; diff --git a/test/WireMock.Net.Tests/WireMockServer.Admin.cs b/test/WireMock.Net.Tests/WireMockServer.Admin.cs index eb9412d09..30e245514 100644 --- a/test/WireMock.Net.Tests/WireMockServer.Admin.cs +++ b/test/WireMock.Net.Tests/WireMockServer.Admin.cs @@ -648,4 +648,24 @@ public async Task WireMockServer_CreateClient_And_CallAdminSettingsEndpoint() // Assert settings.Should().NotBeNull(); } + + [Fact] + public async Task WireMockServer_WithCustomAdminPath_AdminRequestsNotInLogEntries() + { + // Arrange + var cancellationToken = TestContext.Current.CancellationToken; + using var server = WireMockServer.Start(w => + { + w.Logger = new TestOutputHelperWireMockLogger(output); + w.StartAdminInterface = true; + w.AdminPath = "/custom/__admin"; + }); + var client = server.CreateClient(); + + // Act + await client.GetAsync($"{server.Url}/custom/__admin/settings", cancellationToken); + + // Assert + server.LogEntries.Should().BeEmpty(); + } } \ No newline at end of file