Compare commits

..
91 Commits
Author SHA1 Message Date
Stef Heyenrath 69ea61e42c 2.21.0 2026-10-28 17:54:52 +01:00
Stef Heyenrath bc65bccce5 Update Scriban to 7.5.3 (#1526) 2026-10-28 17:52:09 +01:00
Stef Heyenrath f6dc299ecb 2.20.0 2026-10-23 17:54:30 +02:00
Mattias SundströmandMattias dc14136492 Add near-miss diagnostics and request count/unmatched endpoints to the admin API (#1521)
* Add near-miss diagnostics and request count/unmatched admin endpoints (#171)

Add IWireMockServer.FindUnmatchedLogEntries, FindNearMisses and
FindNearMissesForUnmatchedRequests, which rank the mappings that
partially match a request by score.

Expose them through the admin API (POST requests/count,
GET requests/unmatched, GET requests/unmatched/near-misses and
GET requests/{guid}/near-misses), the RestClient and the OpenAPI document.

* Make scenario delay tests independent of server warm-up time

The two *_StateTransitions_BeforeDelayCompletes tests waited a fixed
100 ms for the scenario state to change, which fails on a cold server.
Poll server.Scenarios until the expected state is reached instead.

---------

Co-authored-by: Mattias <13809209+Quattordici@users.noreply.github.com>
2026-10-23 17:48:54 +02:00
Stef Heyenrath c94824bd64 Update Generate-ReleaseNotes.cmd 2026-10-19 19:30:12 +02:00
Stef HeyenrathandCopilot Autofix powered by AI 54e16fc045 Potential fix for code scanning alert no. 8: Workflow does not contain permissions (#1523)
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2026-10-19 19:25:01 +02:00
Stef Heyenrath 7d0de75ab1 Downgrade JsonPath.Net from 3.0.2 to 2.2.0 to avoid Open Source Maintenance Fee issues (#1522) 2026-10-19 18:42:43 +02:00
Stef Heyenrath afb01a0fa6 Bump MessagePack from 2.5.302 to 2.5.303 (#1519) 2026-10-18 10:51:38 +02:00
Mattias SundströmandMattias 0132ec873a Add ScribanRegistrationCallback to register custom Scriban functions (#1515) (#1516)
- Add WireMockServerSettings.ScribanRegistrationCallback
  (Action<object, IFileSystemHandler>?) so code-configured servers can add
  functions, objects or variables to Scriban and Scriban-DotLiquid templates.
- Pass the settings to ScribanContextFactory, which invokes the callback with
  a new ScriptObject per transformer context.
- Push the custom globals beneath the template model in ScribanContext so
  request, response, mapping and data take precedence; keep the original
  Template.Render call when no callback is configured.
- Add the strongly typed WithScribanRegistration extension method.
- Add a Scriban example to the .NET 8 console app.
- Make the proxy tests use a local target server instead of www.google.com,
  and skip the IPv6 loopback test when the OS has no IPv6 support.

Co-authored-by: Mattias <13809209+Quattordici@users.noreply.github.com>
2026-10-18 09:29:11 +02:00
Stef Heyenrath 64734c4f92 <VersionPrefix>2.19.0</VersionPrefix> 2026-10-07 18:25:30 +02:00
Stef Heyenrath a5589e1e2f 2.19.0 2026-10-07 18:22:51 +02:00
Stef HeyenrathandStef Heyenrath cb4503b1a6 Fix DateTime in Response (#1514)
* Fix DateTime in Response

* // Set the DateTime from the Response to the current DateTime

---------

Co-authored-by: Stef Heyenrath <stef.heyenrath@apg.nl>
2026-10-07 17:35:58 +02:00
Stef Heyenrath dd762f218b Use IDateTimeUtils in WireMockWebSocketContext (#1512) 2026-10-06 22:58:33 +02:00
Stef Heyenrath b0f9924933 Add example /test (#1513) 2026-10-06 22:51:17 +02:00
Stef Heyenrath dc4cdfae50 2.18.0 2026-09-22 22:21:50 +02:00
Stef Heyenrath f19d94acae 2.18.0 2026-09-22 22:18:47 +02:00
Stef Heyenrath 90819f031e WireMock.Net.OpenApiParser: fix depencency on RamlToOpenApiConverter.SourceOnly (#1510) 2026-09-22 22:16:13 +02:00
Stef Heyenrath 471745f704 2.17.0 2026-09-21 20:53:22 +02:00
Stef Heyenrath 7db5b562d8 Revert to RamlToOpenApiConverter.SourceOnly (#1509) 2026-09-21 20:50:49 +02:00
Stef Heyenrath e4389333a1 2.16.0 2026-09-19 07:54:10 +02:00
Stef HeyenrathandCopilot Autofix powered by AI f0969e9618 Upgrade dependency RamlToOpenApiConverter to version 0.40.0 (#1505)
* Upgrade dependency RamlToOpenApiConverter to version 0.40.0

* fix code comment

* .

* Potential fix for pull request finding 'Constant condition'

Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>

* Potential fix for pull request finding 'Constant condition'

Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>

* fix

---------

Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
2026-09-10 20:28:43 +02:00
John Call 84f4109a7c Update SharpYaml package version to 2.1.5 (#1504)
Fix https://github.com/xoofx/SharpYaml/issues/130
2026-09-05 08:54:06 +02:00
ee0f890795 Fix FormUrlEncodedMatcher (+ refactor values / matchers logic) (#1503)
* Fix FormUrlEncodedMatcher

* tests

* Apply suggestions from code review

Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* fix comments

---------

Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-08-20 17:31:16 +02:00
Dmytro Nikitin 2b165eb6a1 Fix #1501: Generate WhenStateIs/WillSetStateTo correctly in ToCSharpCode (#1502)
* Fix #1501: Generate WhenStateIs/WillSetStateTo correctly in ToCSharpCode

* Fix matcher to code converters. Refactor changes.

* Adjust test artifacts.

* Return fully qualified class names for matchers
2026-08-19 19:58:47 +02:00
Stef Heyenrath 42353f035e 2.15.0 2026-08-15 09:51:50 +02:00
Stef Heyenrath 7e9718eab7 Update Testcontainers (#1499) 2026-08-14 16:55:57 +02:00
Dmytro Nikitin a0df0916f9 Fix #1496: WithParam RejectOnMatch inverting the match result (#1497) 2026-08-13 20:42:06 +02:00
Dmytro Nikitin 7a8582fef5 Fix #1494: WithClientIP(MatchOperator, IStringMatcher[]) ignores the operator (#1495) 2026-08-09 18:49:13 +02:00
Stef Heyenrath 05929bdea6 remove copilot-setup-steps.yml 2026-08-06 20:15:30 +02:00
Stef Heyenrath 84c32d5b2d 2.14.0 2026-08-06 19:32:33 +02:00
Dmytro Nikitin d39f7c771f Fix #1492: Serialize WithClientIP matcher to Request.ClientIP (#1493) 2026-08-06 19:28:40 +02:00
Stef Heyenrath 97c8e4a08a 2.13.0 2026-07-19 17:04:12 +02:00
Stef Heyenrath f5eb01c580 2.13.0 2026-07-19 09:59:27 +02:00
Stef Heyenrath 8260dba641 Add some more tests for WireMockList + move AdminPaths.cs file (#1491) 2026-07-18 09:48:41 +02:00
Stef Heyenrath 5e1c126e34 Pipeline parameter : runWindowsReleaseToMyGet 2026-07-18 09:48:23 +02:00
ca4eea808e Fix: Fully respect configured custom admin path (merge PR #1483) (#1490)
* Fix: Fully respect configured custom admin path

* Add tests for custom admin path behaviour

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Peter Benko <peter.benko@gmail.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Stef Heyenrath <Stef.Heyenrath@gmail.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-07-18 09:01:04 +02:00
Stef Heyenrath 5692498ea3 Swagger (#1489)
* Add openapi swagger yml

* fix

* version

* reloadStaticMappings
2026-07-17 18:59:19 +02:00
СтепанandМинин Степан Александрович d270ad7f4f feat(xunit logging): TestOutputHelperWireMockLogger (#1488)
* feat(xunit logging): TestOutputHelperWireMockLogger

allow to provide current test output helper

* fix(ctor): TestOutputHelperWireMockLogger

made factory return nullable

---------

Co-authored-by: Минин Степан Александрович <minin_s@goldapple.ru>
2026-07-17 18:25:25 +02:00
Stef Heyenrath 345efe8cfc 2.12.0 2026-07-07 21:38:12 +02:00
Stef Heyenrath a74468e780 When Response.Create() is called without a pre-built ResponseMessage, use the DateTime.UtcNow (#1487) 2026-07-07 21:36:11 +02:00
John Call 041160d824 Update Scriban.Signed package version to 7.2.5 (#1484)
* Update Scriban.Signed package version to 7.2.6

* Downgrade Scriban.Signed package version to 7.2.5
2026-07-07 20:54:11 +02:00
Stef Heyenrath 4666757df9 Upgrade RamlToOpenApiConverter.SourceOnly to 0.21.0 (#1480) 2026-07-03 21:04:58 +02:00
Stef Heyenrath 49dcbda647 Upgrade Microsoft.OpenApi to 3.7.0 and YamlDotNet to 18.1.0 (#1479)
* Upgrade Microsoft.OpenApi to 3.7.0 and YamlDotNet to 18.1.0

* fix
2026-07-01 22:44:34 +02:00
Stef Heyenrath cbc28c235c 2.11.0 2026-06-12 20:09:27 +02:00
Stef Heyenrath dd1aa5139e Fix BodyParser (#1476) 2026-06-12 20:04:23 +02:00
Stef Heyenrath 89b3149732 Update NuGet packages (Aspire, MessagePack and more) (#1475) 2026-06-12 12:01:09 +02:00
Stef Heyenrath d2b794ec5a 2.10.0 2026-06-07 22:52:32 +02:00
Stef Heyenrath 921a3ebc63 Update JsonConverter to 0.13.0 (#1472) 2026-06-07 22:37:55 +02:00
Stef Heyenrath 428657a97e 2.9.0 2026-06-02 22:43:03 +02:00
Stef Heyenrath fe9745235a Fix WireMock.Net.Aspire project when using Aspire 13.4.0 (#1470) 2026-06-02 22:39:32 +02:00
Stef Heyenrath 1264df4a72 2.8.0 2026-05-31 09:24:23 +02:00
Stef Heyenrath 5b5d68be86 readme: Build GitHub 2026-05-30 09:13:18 +02:00
Stef Heyenrath e92041783c Update JsonMatcher to support IgnoreArrayOrder (#1468)
* Update JsonMatcher to support IgnoreArrayOrder

* more tests
2026-05-30 08:45:52 +02:00
dependabot[bot]andcopilot-swe-agent[bot] 6912d52faf Bump System.Text.RegularExpressions from 4.3.0 to 4.3.1 (#1466)
---
updated-dependencies:
- dependency-name: System.Text.RegularExpressions
  dependency-version: 4.3.1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-05-29 20:53:08 +02:00
dependabot[bot] 3685fccfaf Bump System.Net.Http from 4.3.0 to 4.3.4 (#1467)
---
updated-dependencies:
- dependency-name: System.Net.Http
  dependency-version: 4.3.4
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-29 20:40:44 +02:00
36b0a93a6c Add SystemTextJsonMatchers (#1447)
* SystemTextJsonMatcher

* ,

* .

* new projectx

* Update test/WireMock.Net.Tests/Pact/PactTests.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update test/WireMock.Net.Tests/WebSockets/WebSocketIntegrationTests.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/WireMock.Net/WireMock.Net.csproj

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/WireMock.Net.Minimal/Properties/AssemblyInfo.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* .

* more tests

* .

* .

* x

* ...

* .

* fix tests

* 0.11.0

* .

* delete jsonutils.cs

* s

* fix findings

* Apply suggestions from code review

Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>

* Potential fix for pull request finding 'Missing Dispose call on local IDisposable'

Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>

* JsonConverter 0.12.0

* -- tools

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
2026-05-29 20:19:51 +02:00
Stef Heyenrath fed1c87663 2.7.0 2026-05-24 10:25:30 +02:00
Stef Heyenrath e2e83abeb5 Trusted Publishing (#1465)
* Trusted Publishing

* push:

* "

* pwsh

* pack

* push:

* Trusted Publishing = OK
2026-05-24 10:21:02 +02:00
Stef Heyenrath 3aef0ad7a2 Update Scriban.Signed to latest (#1462) 2026-05-23 21:53:32 +02:00
Stef Heyenrath e68a73c3d5 Remove MultipartUploader project 2026-05-23 21:44:33 +02:00
Stef Heyenrath bd83a630ff WireMock.Net.Service 2026-05-23 21:42:34 +02:00
Michi 52ac7e37dc Update Testcontainers to 4.12.0 (#1461) 2026-05-23 20:46:52 +02:00
Michaël 0eccf43a8e chore: update Handlebars 2.5.2 to 2.5.5 #1458 (#1459) 2026-05-23 20:26:36 +02:00
fa423370b1 Update OpenTelemetry.Api from 1.14.0 to 1.15.3 in unit test project (#1457)
* Bump OpenTelemetry.Api from 1.14.0 to 1.15.3

---
updated-dependencies:
- dependency-name: OpenTelemetry.Api
  dependency-version: 1.15.3
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

* chore: bump additional OpenTelemetry packages in UsingNuGet test project

Agent-Logs-Url: https://github.com/wiremock/WireMock.Net/sessions/bc8104f3-74b0-4a40-ac16-e404cbc230df

Co-authored-by: StefH <249938+StefH@users.noreply.github.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: StefH <249938+StefH@users.noreply.github.com>
2026-05-12 13:23:21 +02:00
Stef Heyenrath 1806ae39f8 2.6.0 2026-05-11 10:31:09 +02:00
Peter Benko 67acdcf1d3 Fix request storing when RequestLogExpirationDuration is set [bug] (#1455)
* Fix response timestamp

* Extracted new interface to own file
2026-05-11 10:28:38 +02:00
Stef Heyenrath 4bb378bdce 2.5.0 2026-05-04 18:52:29 +02:00
Stef Heyenrath f9df0d0ee9 Fix name in RequestMessageHeaderMatcher 2026-05-03 09:41:25 +02:00
Степан f8d3b51fbc Feature/early mismatch (#1451)
* feat(request matchers): Add support for early mismatch in mapping processing

* test(request matchers): Add unit test for early mismatch functionality

* test(grpc): Add test for grpc requests early mismatch and error logging (Issue #1442)

* feat(request matchers): RequestMatcherType

Add `RequestMatcherType` to request matchers for improved type
identification

Closes #1442

* refactor(request matchers): Request

Replace `EarlyMatcherSelector` with `EarlyMatcherType` for improved
clarity and consistency

Closes #1442

* feat(request): conversion

Add EarlyMatcherType support in request models and mapping conversion

Closes #1442

* test(mapping): new tests

add unit tests for EarlyMatcherType in mapping conversion and
serialization

Closes #1442

* refactor(request matchers): RequestMessageEarlyMatcher

Replaced inline `EarlyMatcherType` logic with the new
`RequestMessageEarlyMatcher` class to support cases when several
matchers of the same type are present. For instance - Header, Cookie,
Param

Closes #1442

* test(request matchers): Early Mismatch

add unit tests for early mismatch scenarios with several matchers of
same type. Currently, headers and parameters

Closes #1442

* refactor(mapping): RequestModel.EarlyMatcherType

use fully qualified enum for EarlyMatcherType in serialization

Closes #1442

* style(review): fixes

- removed unused method
- added missing curly brackets

Closes #1442
2026-05-03 09:27:19 +02:00
CopilotandStefH be9864461d Fix CVE-2026-40021: upgrade log4net to 3.3.0 in examples/WireMock.Net.Service/packages.config (#1453)
* Initial plan

* Fix CVE-2026-40021: update log4net to 3.3.0 in packages.config

Agent-Logs-Url: https://github.com/wiremock/WireMock.Net/sessions/a09a4576-1b17-41fe-9912-c1efdf922fed

Co-authored-by: StefH <249938+StefH@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: StefH <249938+StefH@users.noreply.github.com>
2026-05-02 19:46:13 +02:00
dependabot[bot] 4bb7e8af45 Bump log4net from 2.0.15 to 3.3.0 (#1452)
---
updated-dependencies:
- dependency-name: log4net
  dependency-version: 3.3.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-02 18:30:43 +02:00
Stef Heyenrath 8bf42904ab 2.4.0 2026-04-24 16:48:28 +02:00
0a48b40021 Bump OpenTelemetry.Exporter.OpenTelemetryProtocol from 1.14.0 to 1.15.x (#1450)
* Bump OpenTelemetry.Exporter.OpenTelemetryProtocol from 1.14.0 to 1.15.3

---
updated-dependencies:
- dependency-name: OpenTelemetry.Exporter.OpenTelemetryProtocol
  dependency-version: 1.15.3
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

* Upgrade all OpenTelemetry related NuGet packages to latest versions

Agent-Logs-Url: https://github.com/wiremock/WireMock.Net/sessions/5b2f4449-ec3f-49f1-afbe-654e19a97a33

Co-authored-by: StefH <249938+StefH@users.noreply.github.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: StefH <249938+StefH@users.noreply.github.com>
2026-04-24 08:34:00 +02:00
Jayaraman Venkatesan 1962437dcd Added feature to enable and disable mappings (#1437)
* feat/1421 added feature to enable and disable mappings

* feat/1421 updated test constants to reflect 2 new admin endpoints /enable and /disable

* feat/1421 updated tests to fix flakyness - removed delay before assertion that is causing upstream connection from proxy to teardown prematurely before test ends

* feat/1421 addressing PR comments - Updated logic to represent IsDisable insted of IsEnabled
2026-04-24 08:07:37 +02:00
Stef Heyenrath 85d61a1877 2.3.0 2026-04-20 18:53:59 +02:00
Stef Heyenrath 885911203b Use DefaultJsonSerializer for BodyAsJson-Response (#1448) 2026-04-18 09:43:24 +02:00
Stef Heyenrath 1e591d5f8a Fix ExactMatcher and JsonMatcher not working for ISO dates as string (#1443) 2026-04-17 13:32:26 +02:00
Stef Heyenrath 02b7e3744e Update instructions.md (#1444) 2026-04-17 13:23:29 +02:00
dependabot[bot] 6e2a4d7e04 Bump log4net from 2.0.15 to 3.3.0 (#1440)
---
updated-dependencies:
- dependency-name: log4net
  dependency-version: 3.3.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-16 07:49:17 +02:00
Jayaraman VenkatesanandStef Heyenrath 479bb0b8ec bug/wiremock-1268 moving Scenario state change before global response delay (#1436)
Co-authored-by: Stef Heyenrath <Stef.Heyenrath@gmail.com>
2026-04-03 10:51:24 +02:00
Stef Heyenrath a453e00fdb Fix WireMock.Net.WebApplication.IIS example (#1435) 2026-03-31 22:52:27 +02:00
Stef Heyenrath 3214c2ebc7 2.2.0 2026-03-30 19:51:09 +02:00
Stef Heyenrath 6c6a42979e Add comments for ScenarioStateStore related code (#1433) 2026-03-30 19:49:28 +02:00
Stef Heyenrath b4f5b9256c Upgrade Scriban.Signed (#1434) 2026-03-30 19:49:16 +02:00
Stef Heyenrath 070e4b6ab9 2.1.0 2026-03-29 13:16:23 +02:00
m4tchl0ck f919929cb7 Add injectable IScenarioStateStore for distributed scenario state (#1430)
* Move ScenarioState to Abstractions and add IScenarioStateStore interface

ScenarioState is moved to the Abstractions project so it can be referenced
by the new IScenarioStateStore interface. The interface defines the contract
for storing and retrieving scenario states, enabling distributed implementations.

* Add InMemoryScenarioStateStore default implementation

Wraps ConcurrentDictionary with OrdinalIgnoreCase comparer, preserving
exact current behavior. The Update method encapsulates read-modify-write
so distributed implementations can make it atomic.

* Wire IScenarioStateStore into middleware options, settings, and consumers

Replace direct ConcurrentDictionary<string, ScenarioState> usage with
IScenarioStateStore across all consumer files. The store is injectable
via WireMockServerSettings.ScenarioStateStore, defaulting to the
InMemoryScenarioStateStore for backward compatibility.

* Add FileBasedScenarioStateStore for persistent scenario state

In-memory ConcurrentDictionary backed by JSON file persistence in
__admin/scenarios/. Reads from cache, mutations write through to disk.
Constructor loads existing state from disk on startup.

* Make ScenarioStateStore non-nullable with default InMemoryScenarioStateStore

Move InMemoryScenarioStateStore from WireMock.Net.Minimal to
WireMock.Net.Shared so it lives alongside WireMockServerSettings.
This allows WireMockServerSettings.ScenarioStateStore to be
non-nullable with a default value, following the same pattern as
DefaultJsonSerializer. The null-coalescing fallback in
WireMockMiddlewareOptionsHelper is no longer needed.
2026-03-25 13:04:44 +01:00
cdd33695e5 Add helpers for query params fluent MappingModelBuilder (#1425)
* Add helpers for query params

* add example with query params

* add fluent helpers for WithHeaders and WithCookies

---------

Co-authored-by: Logan Dam <Logan.Dam@rabobank.com>
Co-authored-by: Stef Heyenrath <Stef.Heyenrath@gmail.com>
2026-03-14 10:39:30 +01:00
Stef Heyenrath c4caa25eb6 Fix WithWebSocketProxy_Should_Proxy_Binary_Messages 2026-03-14 10:39:04 +01:00
Stef Heyenrath ca788cb9b0 Add WireMockAspNetCoreLogger to log Kestrel warnings/errors (#1432)
* Add WireMockAspNetCoreLogger

* fix tests

* x

* .
2026-03-14 10:30:56 +01:00
Stef Heyenrath d08ce944b6 Fix WireMockLogger implementation in dotnet-WireMock (#1431) 2026-03-13 18:34:47 +01:00
Stef Heyenrath 0a9f37e857 readme 2026-03-11 18:14:09 +01:00
311 changed files with 13514 additions and 3192 deletions

No files matched your search

+2 -1
View File
@@ -1,4 +1,5 @@
# Copilot Instructions
## Project Guidelines
- All new byte[xx] calls should use using var data = ArrayPool<byte>.Shared.Lease(xx); instead of directly allocating byte arrays
- When running tests in this workspace, do not run tests for the net48 target framework.
- When changing System.Text.Json code in this workspace, verify API availability for netstandard2.0 and netstandard2.1 instead of assuming newer APIs exist.
+15
View File
@@ -0,0 +1,15 @@
---
description: 'Guidelines for this solution'
applyTo: '**/*.csproj, **/*.cs'
---
# Multi .NET Framework Targeting
## Instructions
- The main project "WireMock.Net.Minimal" targets `netstandard2.0` and `net8.0`. Ensure that any new code or dependencies are compatible with these frameworks.
# C# Guidelines
## Instructions
- When a new ByteArray is needed, do not use `var data = new byte[bufferSize];`. Always use `var data = ArrayPool<byte>.Shared.Lease(bufferSize);`.
+40
View File
@@ -0,0 +1,40 @@
name: Publish to NuGet
on:
workflow_dispatch:
jobs:
publish:
name: Build, Pack, and Publish
runs-on: windows-2022
permissions:
id-token: write # enable GitHub OIDC token issuance for this job
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Build projects
shell: pwsh
run: |
Get-ChildItem ./src -Recurse -Filter *.csproj |
ForEach-Object {
dotnet build $_.FullName -c Release
}
- name: Pack projects
shell: pwsh
run: |
Get-ChildItem ./src -Recurse -Filter *.csproj |
ForEach-Object {
dotnet pack $_.FullName -c Release --no-build -o ./packages
}
- name: NuGet login (OIDC → temp API key)
uses: NuGet/login@v1
id: login
with:
user: ${{ secrets.NUGET_USER }}
- name: Push to NuGet
run: dotnet nuget push "**/packages/*.nupkg" --api-key ${{steps.login.outputs.NUGET_API_KEY}} --source "https://api.nuget.org/v3/index.json" --skip-duplicate
+3
View File
@@ -8,6 +8,9 @@ on:
branches:
- 'main'
permissions:
contents: read
jobs:
windows-build-and-run:
name: Run Tests on Windows
-36
View File
@@ -1,36 +0,0 @@
name: "Copilot Setup Steps"
# Automatically run the setup steps when they are changed to allow for easy validation, and
# allow manual testing through the repository's "Actions" tab
on:
workflow_dispatch:
push:
paths:
- .github/workflows/copilot-setup-steps.yml
pull_request:
paths:
- .github/workflows/copilot-setup-steps.yml
jobs:
# The job MUST be called `copilot-setup-steps` or it will not be picked up by Copilot.
copilot-setup-steps:
runs-on: ubuntu-latest
# Set the permissions to the lowest permissions possible needed for your steps.
# Copilot will be given its own token for its operations.
permissions:
# If you want to clone the repository as part of your setup steps, for example to install dependencies, you'll need the `contents: read` permission. If you don't clone the repository in your setup steps, Copilot will do this for you automatically after the steps complete.
contents: read
# You can define any steps you want, and they will run before the agent starts.
# If you do not check out your code, Copilot will do this for you.
steps:
- name: Install .NET 10.x
uses: actions/setup-dotnet@v5
with:
dotnet-version: |
10.x
dotnet-quality: preview
- name: dotnet --info
run: dotnet --info
+121 -2
View File
@@ -1,3 +1,122 @@
# 2.21.0 (28 October 2026)
- [#1526](https://github.com/wiremock/WireMock.Net/pull/1526) - Update Scriban to 7.5.3 [dependencies] contributed by [StefH](https://github.com/StefH)
- [#1525](https://github.com/wiremock/WireMock.Net/issues/1525) - High security vulnerability in Scriban package [security]
# 2.20.0 (23 October 2026)
- [#1516](https://github.com/wiremock/WireMock.Net/pull/1516) - Add ScribanRegistrationCallback to register custom Scriban functions [feature] contributed by [Quattordici](https://github.com/Quattordici)
- [#1519](https://github.com/wiremock/WireMock.Net/pull/1519) - Bump MessagePack from 2.5.302 to 2.5.303 [dependencies] contributed by [StefH](https://github.com/StefH)
- [#1521](https://github.com/wiremock/WireMock.Net/pull/1521) - Add near-miss diagnostics and request count/unmatched endpoints to the admin API [feature] contributed by [Quattordici](https://github.com/Quattordici)
- [#1522](https://github.com/wiremock/WireMock.Net/pull/1522) - Downgrade JsonPath.Net from 3.0.2 to 2.2.0 to avoid Open Source Maintenance Fee issues [dependencies] contributed by [StefH](https://github.com/StefH)
- [#1515](https://github.com/wiremock/WireMock.Net/issues/1515) - Allow registering custom Scriban functions (ScribanRegistrationCallback), like HandlebarsRegistrationCallback [feature]
- [#1520](https://github.com/wiremock/WireMock.Net/issues/1520) - json-everything sub-dependency now has Open Source Maintenance Fee [dependencies]
# 2.19.0 (07 October 2026)
- [#1512](https://github.com/wiremock/WireMock.Net/pull/1512) - Use IDateTimeUtils in WireMockWebSocketContext [bug] contributed by [StefH](https://github.com/StefH)
- [#1514](https://github.com/wiremock/WireMock.Net/pull/1514) - Fix DateTime in Response [bug] contributed by [StefH](https://github.com/StefH)
- [#1511](https://github.com/wiremock/WireMock.Net/issues/1511) - Incorrect response datetime for mapped response [bug]
# 2.18.0 (22 September 2026)
- [#1510](https://github.com/wiremock/WireMock.Net/pull/1510) - WireMock.Net.OpenApiParser: fix depencency on RamlToOpenApiConverter.SourceOnly [bug] contributed by [StefH](https://github.com/StefH)
- [#1508](https://github.com/wiremock/WireMock.Net/issues/1508) - Transitive Dependency Conflict with Microsoft.OpenApi in 2.16.0 [bug]
# 2.17.0 (21 September 2026)
- [#1509](https://github.com/wiremock/WireMock.Net/pull/1509) - Revert to RamlToOpenApiConverter.SourceOnly [bug] contributed by [StefH](https://github.com/StefH)
# 2.16.0 (19 September 2026)
- [#1502](https://github.com/wiremock/WireMock.Net/pull/1502) - Fix #1501: Generate WhenStateIs/WillSetStateTo correctly in ToCSharpCode contributed by [Bafyn](https://github.com/Bafyn)
- [#1503](https://github.com/wiremock/WireMock.Net/pull/1503) - Fix FormUrlEncodedMatcher (+ refactor values / matchers logic) contributed by [StefH](https://github.com/StefH)
- [#1504](https://github.com/wiremock/WireMock.Net/pull/1504) - Update SharpYaml package to version 2.5.1 [dependencies] contributed by [johnthcall](https://github.com/johnthcall)
- [#1505](https://github.com/wiremock/WireMock.Net/pull/1505) - Upgrade dependency RamlToOpenApiConverter to version 0.40.0 [dependencies] contributed by [StefH](https://github.com/StefH)
- [#1500](https://github.com/wiremock/WireMock.Net/issues/1500) - Sending a form-urlencoded body with a repeated field name returns HTTP 500 [bug]
- [#1501](https://github.com/wiremock/WireMock.Net/issues/1501) - Generated C# code for stateful (Scenario) mappings does not compile and drops the WhenStateIs condition [bug]
# 2.15.0 (15 August 2026)
- [#1495](https://github.com/wiremock/WireMock.Net/pull/1495) - Fix #1494: `WithClientIP(MatchOperator, params IStringMatcher[])` ignores the MatchOperator argument [bug] contributed by [Bafyn](https://github.com/Bafyn)
- [#1497](https://github.com/wiremock/WireMock.Net/pull/1497) - Fix WithParam RejectOnMatch inverting the match result #1496 [bug] contributed by [Bafyn](https://github.com/Bafyn)
- [#1499](https://github.com/wiremock/WireMock.Net/pull/1499) - Update Testcontainers [bug] contributed by [StefH](https://github.com/StefH)
- [#1494](https://github.com/wiremock/WireMock.Net/issues/1494) - `WithClientIP(MatchOperator, params IStringMatcher[])` ignores the `MatchOperator` argument [bug]
- [#1496](https://github.com/wiremock/WireMock.Net/issues/1496) - `WithParam(key, MatchBehaviour.RejectOnMatch, values)` inverts the match result [bug]
# 2.14.0 (06 August 2026)
- [#1493](https://github.com/wiremock/WireMock.Net/pull/1493) - Fix #1492: Serialize WithClientIP matcher to Request.ClientIP [bug] contributed by [Bafyn](https://github.com/Bafyn)
- [#1492](https://github.com/wiremock/WireMock.Net/issues/1492) - `WithClientIP` matcher is lost / corrupted when a mapping is serialized [bug]
# 2.13.0 (19 July 2026)
- [#1488](https://github.com/wiremock/WireMock.Net/pull/1488) - feat(xunit logging): TestOutputHelperWireMockLogger [feature] contributed by [Stepami](https://github.com/Stepami)
- [#1490](https://github.com/wiremock/WireMock.Net/pull/1490) - Fix: Fully respect configured custom admin path (merge PR #1483) [feature] contributed by [Copilot](https://github.com/apps/copilot-swe-agent)
- [#1482](https://github.com/wiremock/WireMock.Net/issues/1482) - WireMock.Minimal.Owin does not respect custom pathing for determining admin paths [bug]
# 2.12.0 (07 July 2026)
- [#1479](https://github.com/wiremock/WireMock.Net/pull/1479) - Upgrade Microsoft.OpenApi to 3.7.0 and YamlDotNet to 18.1.0 [security] contributed by [StefH](https://github.com/StefH)
- [#1480](https://github.com/wiremock/WireMock.Net/pull/1480) - Upgrade RamlToOpenApiConverter.SourceOnly to 0.21.0 [feature] contributed by [StefH](https://github.com/StefH)
- [#1484](https://github.com/wiremock/WireMock.Net/pull/1484) - Update Scriban.Signed package version to 7.2.5 [security] contributed by [johnthcall](https://github.com/johnthcall)
- [#1487](https://github.com/wiremock/WireMock.Net/pull/1487) - Response.Create() sets the DateTime to UtcNow [bug] contributed by [StefH](https://github.com/StefH)
- [#1486](https://github.com/wiremock/WireMock.Net/issues/1486) - ResponseMessage.DateTime defaults to DateTime.MinValue, causing premature log entry purging for *matched* requests [bug]
# 2.11.0 (12 June 2026)
- [#1475](https://github.com/wiremock/WireMock.Net/pull/1475) - Update NuGet packages (Aspire, MessagePack and more) [security] contributed by [StefH](https://github.com/StefH)
- [#1476](https://github.com/wiremock/WireMock.Net/pull/1476) - Fix BodyParser [bug] contributed by [StefH](https://github.com/StefH)
- [#1473](https://github.com/wiremock/WireMock.Net/issues/1473) - JsonPartialWildCardMatcher With DateTime Regex Stopped Matching (Regression?) [bug]
# 2.10.0 (07 June 2026)
- [#1472](https://github.com/wiremock/WireMock.Net/pull/1472) - Update JsonConverter to 0.13.0 [bug] contributed by [StefH](https://github.com/StefH)
# 2.9.0 (02 June 2026)
- [#1470](https://github.com/wiremock/WireMock.Net/pull/1470) - Fix WireMock.Net.Aspire project when using Aspire 13.4.0 [bug] contributed by [StefH](https://github.com/StefH)
- [#1469](https://github.com/wiremock/WireMock.Net/issues/1469) - WireMock.Net doesn't play well with Aspire 13.4.0 [bug]
# 2.8.0 (31 May 2026)
- [#1447](https://github.com/wiremock/WireMock.Net/pull/1447) - Add SystemTextJsonMatchers [feature] contributed by [StefH](https://github.com/StefH)
- [#1466](https://github.com/wiremock/WireMock.Net/pull/1466) - Bump System.Text.RegularExpressions from 4.3.0 to 4.3.1 in Aspire Example project [dependencies, .NET] contributed by [dependabot[bot]](https://github.com/apps/dependabot)
- [#1467](https://github.com/wiremock/WireMock.Net/pull/1467) - Bump System.Net.Http from 4.3.0 to 4.3.4 in Aspire Example project [dependencies, .NET] contributed by [dependabot[bot]](https://github.com/apps/dependabot)
- [#1468](https://github.com/wiremock/WireMock.Net/pull/1468) - Update JsonMatcher to support IgnoreArrayOrder [feature] contributed by [StefH](https://github.com/StefH)
- [#1439](https://github.com/wiremock/WireMock.Net/issues/1439) - JSON reference properties cannot be used when using System.Text.Json serialization [bug]
- [#1445](https://github.com/wiremock/WireMock.Net/issues/1445) - Configurable JSON DE-serialization support (Newtonsoft.Json vs System.Text.Json) [feature]
- [#1460](https://github.com/wiremock/WireMock.Net/issues/1460) - Option to ignore array order when comparing json body [feature]
# 2.7.0 (24 May 2026)
- [#1457](https://github.com/wiremock/WireMock.Net/pull/1457) - Update OpenTelemetry.Api from 1.14.0 to 1.15.3 in unit test project [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot)
- [#1459](https://github.com/wiremock/WireMock.Net/pull/1459) - chore: update Handlebars 2.5.2 to 2.5.5 [dependencies] contributed by [kamisoft-fr](https://github.com/kamisoft-fr)
- [#1461](https://github.com/wiremock/WireMock.Net/pull/1461) - Update Testcontainers nuget package to 4.12.0 [dependencies] contributed by [MD-V](https://github.com/MD-V)
- [#1462](https://github.com/wiremock/WireMock.Net/pull/1462) - Update Scriban.Signed to latest [dependencies] contributed by [StefH](https://github.com/StefH)
- [#1458](https://github.com/wiremock/WireMock.Net/issues/1458) - update Handlebars Humanizer package version [feature]
# 2.6.0 (11 May 2026)
- [#1455](https://github.com/wiremock/WireMock.Net/pull/1455) - Fix request storing when RequestLogExpirationDuration is set [bug] [bug] contributed by [pbenko-xitaso](https://github.com/pbenko-xitaso)
- [#1454](https://github.com/wiremock/WireMock.Net/issues/1454) - No requests stored in Standalone when RequestLogExpirationDuration is set [bug]
# 2.5.0 (04 May 2026)
- [#1451](https://github.com/wiremock/WireMock.Net/pull/1451) - Feature/early mismatch [feature] contributed by [Stepami](https://github.com/Stepami)
- [#1452](https://github.com/wiremock/WireMock.Net/pull/1452) - Bump log4net from 2.0.15 to 3.3.0 in example console app [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot)
- [#1453](https://github.com/wiremock/WireMock.Net/pull/1453) - Fix CVE-2026-40021: upgrade log4net to 3.3.0 in examples/WireMock.Net.Service/packages.config [dependencies] contributed by [Copilot](https://github.com/apps/copilot-swe-agent)
- [#1442](https://github.com/wiremock/WireMock.Net/issues/1442) - Bug: [grpc] WithBodyAsProtoBuf exception on match [bug]
# 2.4.0 (24 April 2026)
- [#1437](https://github.com/wiremock/WireMock.Net/pull/1437) - Added feature to enable and disable mappings [feature] contributed by [jayaraman-venkatesan](https://github.com/jayaraman-venkatesan)
- [#1450](https://github.com/wiremock/WireMock.Net/pull/1450) - Bump OpenTelemetry.Exporter.OpenTelemetryProtocol from 1.14.0 to 1.15.x [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot)
- [#1421](https://github.com/wiremock/WireMock.Net/issues/1421) - Deactivate mapping without deleting it [feature]
# 2.3.0 (20 April 2026)
- [#1436](https://github.com/wiremock/WireMock.Net/pull/1436) - Moving Scenario state change before global response delay is set [feature] contributed by [jayaraman-venkatesan](https://github.com/jayaraman-venkatesan)
- [#1440](https://github.com/wiremock/WireMock.Net/pull/1440) - Bump log4net from 2.0.15 to 3.3.0 [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot)
- [#1443](https://github.com/wiremock/WireMock.Net/pull/1443) - Fix ExactMatcher and JsonMatcher not working for ISO dates as string [bug] contributed by [StefH](https://github.com/StefH)
- [#1444](https://github.com/wiremock/WireMock.Net/pull/1444) - Update instructions.md [refactor] contributed by [StefH](https://github.com/StefH)
- [#1448](https://github.com/wiremock/WireMock.Net/pull/1448) - Use DefaultJsonSerializer for BodyAsJson-Response [bug] contributed by [StefH](https://github.com/StefH)
- [#1205](https://github.com/wiremock/WireMock.Net/issues/1205) - System.Private.Uri 4.3.0 Blackduck security High vulnerability [bug]
- [#1260](https://github.com/wiremock/WireMock.Net/issues/1260) - Scenario state change before a response delay timeout ends [bug]
- [#1441](https://github.com/wiremock/WireMock.Net/issues/1441) - ExactMatcher and JsonMatcher not working for ISO dates in v2.2.0 [bug]
- [#1446](https://github.com/wiremock/WireMock.Net/issues/1446) - WithBodyAsJson does not return correctly formatted json when using SystemTextJsonConverter [bug]
# 2.2.0 (30 March 2026)
- [#1433](https://github.com/wiremock/WireMock.Net/pull/1433) - Add comments for ScenarioStateStore related code [refactor] contributed by [StefH](https://github.com/StefH)
- [#1434](https://github.com/wiremock/WireMock.Net/pull/1434) - Upgrade Scriban.Signed [security] contributed by [StefH](https://github.com/StefH)
# 2.1.0 (29 March 2026)
- [#1425](https://github.com/wiremock/WireMock.Net/pull/1425) - Add helpers for query params fluent MappingModelBuilder [feature] contributed by [biltongza](https://github.com/biltongza)
- [#1430](https://github.com/wiremock/WireMock.Net/pull/1430) - Add injectable IScenarioStateStore for distributed scenario state [feature] contributed by [m4tchl0ck](https://github.com/m4tchl0ck)
- [#1431](https://github.com/wiremock/WireMock.Net/pull/1431) - Fix WireMockLogger implementation in dotnet-WireMock [bug] contributed by [StefH](https://github.com/StefH)
- [#1432](https://github.com/wiremock/WireMock.Net/pull/1432) - Add WireMockAspNetCoreLogger to log Kestrel warnings/errors [feature] contributed by [StefH](https://github.com/StefH)
# 2.0.0 (11 March 2026)
- [#1359](https://github.com/wiremock/WireMock.Net/pull/1359) - Version 2.x contributed by [StefH](https://github.com/StefH)
- [#1394](https://github.com/wiremock/WireMock.Net/pull/1394) - MappingSerializer (Newtonsoft or SystemText)-Json [feature] contributed by [StefH](https://github.com/StefH)
@@ -1356,11 +1475,11 @@
- [#145](https://github.com/wiremock/WireMock.Net/pull/145) - Cancellation token not passed to server instance in .NET Core 2 [bug] contributed by [Bob11327](https://github.com/Bob11327)
# 1.0.3.18 (25 May 2018)
- [#142](https://github.com/wiremock/WireMock.Net/pull/142) - Allow all headers to be set as Response headers contributed by [StefH](https://github.com/StefH)
- [#142](https://github.com/wiremock/WireMock.Net/pull/142) - Allow all headers to be set as Response headers [bug] contributed by [StefH](https://github.com/StefH)
- [#122](https://github.com/wiremock/WireMock.Net/issues/122) - WireMock.Net not responding in unit tests - same works in console application
- [#126](https://github.com/wiremock/WireMock.Net/issues/126) - Question: UsingHead always returns 0 for Content-Length header even when explicitly specified
- [#132](https://github.com/wiremock/WireMock.Net/issues/132) - LogEntries not being recorded on subsequent tests
- [#136](https://github.com/wiremock/WireMock.Net/issues/136) - Question: Does the WireMock send Content-Length response header
- [#136](https://github.com/wiremock/WireMock.Net/issues/136) - Question: Does the WireMock send Content-Length response header [bug]
- [#137](https://github.com/wiremock/WireMock.Net/issues/137) - Question: How to specify Transfer-Encoding response header?
- [#139](https://github.com/wiremock/WireMock.Net/issues/139) - Wiki link https://github.com/StefH/WireMock.Net/wiki/Record-(via-proxy)-and-Save is dead
+3 -3
View File
@@ -4,7 +4,7 @@
</PropertyGroup>
<PropertyGroup>
<VersionPrefix>2.0.0</VersionPrefix>
<VersionPrefix>2.21.0</VersionPrefix>
<PackageIcon>WireMock.Net-Logo.png</PackageIcon>
<PackageProjectUrl>https://github.com/wiremock/WireMock.Net</PackageProjectUrl>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
@@ -51,8 +51,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="JetBrains.Annotations" Version="2025.2.4" PrivateAssets="All" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="10.0.103" PrivateAssets="All" />
<PackageReference Include="JetBrains.Annotations" Version="2026.2.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="10.0.403" PrivateAssets="All" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
+12 -2
View File
@@ -1,7 +1,17 @@
rem https://github.com/StefH/GitHubReleaseNotes
SET version=2.0.0
@echo off
setlocal
for /f "usebackq delims=" %%v in (`powershell -NoProfile -Command "[xml]$props = Get-Content 'Directory.Build.props'; $props.Project.PropertyGroup | ForEach-Object { $_.VersionPrefix } | Where-Object { $_ } | Select-Object -First 1"`) do set version=%%v
if "%version%"=="" (
echo Unable to determine VersionPrefix from Directory.Build.props.
exit /b 1
)
GitHubReleaseNotes --output CHANGELOG.md --skip-empty-releases --exclude-labels wontfix test question invalid doc duplicate example environment --version %version% --token %GH_TOKEN%
if errorlevel 1 exit /b %errorlevel%
GitHubReleaseNotes --output PackageReleaseNotes.txt --skip-empty-releases --exclude-labels test question invalid doc duplicate example environment --template PackageReleaseNotes.template --version %version% --token %GH_TOKEN%
GitHubReleaseNotes --output PackageReleaseNotes.txt --skip-empty-releases --exclude-labels wontfix test question invalid doc duplicate example environment --template PackageReleaseNotes.template --version %version% --token %GH_TOKEN%
if errorlevel 1 exit /b %errorlevel%
+3 -6
View File
@@ -1,8 +1,5 @@
# 2.0.0 (11 March 2026)
- #1359 Version 2.x
- #1394 MappingSerializer (Newtonsoft or SystemText)-Json [feature]
- #1341 Configurable JSON serialization support (Newtonsoft.Json vs System.Text.Json) [feature]
- #1422 WireMock.Net seems to be incompatible with Microsoft.Owin.Security.Interop [bug]
- #1424 WireMock.Net.FluentAssertions is incompatible with WireMock.Net.Aspire [feature]
# 2.21.0 (28 October 2026)
- #1526 Update Scriban to 7.5.3 [dependencies]
- #1525 High security vulnerability in Scriban package [security]
The full release notes can be found here: https://github.com/wiremock/WireMock.Net/blob/master/CHANGELOG.md
+5 -2
View File
@@ -39,8 +39,9 @@ A C# .NET version based on [mock4net](https://github.com/alexvictoor/mock4net) w
| | |
| ***Quality*** | &nbsp; |
| &nbsp;&nbsp;**Build Azure** | [![Build Status Azure](https://stef.visualstudio.com/WireMock.Net/_apis/build/status/WireMock.Net)](https://stef.visualstudio.com/WireMock.Net/_build/latest?definitionId=61) |
| &nbsp;&nbsp;**Build GitHub** | [![Run Tests](https://github.com/wiremock/WireMock.Net/actions/workflows/ci.yml/badge.svg)](https://github.com/wiremock/WireMock.Net/actions/workflows/ci.yml)
| &nbsp;&nbsp;**Quality** | [![Sonar Quality Gate](https://sonarcloud.io/api/project_badges/measure?project=WireMock-Net_WireMock.Net&metric=alert_status)](https://sonarcloud.io/project/issues?id=WireMock-Net_WireMock.Net) [![CodeFactor](https://www.codefactor.io/repository/github/wiremock/wiremock.net/badge)](https://www.codefactor.io/repository/github/wiremock/wiremock.net) |
| &nbsp;&nbsp;**Sonar Bugs** | [![Sonar Bugs](https://sonarcloud.io/api/project_badges/measure?project=WireMock-Net_WireMock.Net&metric=bugs)](https://sonarcloud.io/project/issues?id=WireMock-Net_WireMock.Net&resolved=false&types=BUG) [![Sonar Code Smells](https://sonarcloud.io/api/project_badges/measure?project=WireMock-Net_WireMock.Net&metric=code_smells)](https://sonarcloud.io/project/issues?id=WireMock-Net_WireMock.Net&resolved=false&types=CODE_SMELL) |
| &nbsp;&nbsp;**SonarQube** | [![Sonar Bugs](https://sonarcloud.io/api/project_badges/measure?project=WireMock-Net_WireMock.Net&metric=bugs)](https://sonarcloud.io/project/issues?id=WireMock-Net_WireMock.Net&resolved=false&types=BUG) [![Sonar Code Smells](https://sonarcloud.io/api/project_badges/measure?project=WireMock-Net_WireMock.Net&metric=code_smells)](https://sonarcloud.io/project/issues?id=WireMock-Net_WireMock.Net&resolved=false&types=CODE_SMELL) |
| &nbsp;&nbsp;**Coverage** | [![Sonar Coverage](https://sonarcloud.io/api/project_badges/measure?project=WireMock-Net_WireMock.Net&metric=coverage)](https://sonarcloud.io/component_measures?id=WireMock-Net_WireMock.Net&metric=coverage) [![codecov](https://codecov.io/gh/wiremock/WireMock.Net/branch/master/graph/badge.svg)](https://codecov.io/gh/wiremock/WireMock.Net)|
| &nbsp;&nbsp;**TIOBE** | [TIOBE Quality Indicator](https://ticsdemo.tiobe.com/tiobeweb/DEMO/TqiDashboard.html#axes=Project(WireMock.Net),Sub()&metric=tqi)
@@ -64,6 +65,7 @@ A C# .NET version based on [mock4net](https://github.com/alexvictoor/mock4net) w
| | | |
| &nbsp;&nbsp;**WireMock.Net.Extensions.Routing** | [![NuGet Badge WireMock.Net.Extensions.Routing](https://img.shields.io/nuget/v/WireMock.Net.Extensions.Routing)](https://www.nuget.org/packages/WireMock.Net.Extensions.Routing) | [![MyGet Badge WireMock.Net.Extensions.Routing](https://img.shields.io/myget/wiremock-net/vpre/WireMock.Net.Extensions.Routing?includePreReleases=true&label=MyGet)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.Extensions.Routing)
| &nbsp;&nbsp;**WireMock.Net.Matchers.CSharpCode** | [![NuGet Badge WireMock.Net.Matchers.CSharpCode](https://img.shields.io/nuget/v/WireMock.Net.Matchers.CSharpCode)](https://www.nuget.org/packages/WireMock.Net.Matchers.CSharpCode) | [![MyGet Badge WireMock.Net.Matchers.CSharpCode](https://img.shields.io/myget/wiremock-net/vpre/WireMock.Net.Matchers.CSharpCode?includePreReleases=true&label=MyGet)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.Matchers.CSharpCode)
| &nbsp;&nbsp;**WireMock.Net.Matchers.SystemTextJsonPath** | [![NuGet Badge WireMock.Net.Matchers.SystemTextJsonPath](https://img.shields.io/nuget/v/WireMock.Net.Matchers.SystemTextJsonPath)](https://www.nuget.org/packages/WireMock.Net.Matchers.SystemTextJsonPath) | [![MyGet Badge WireMock.Net.Matchers.SystemTextJsonPath](https://img.shields.io/myget/wiremock-net/vpre/WireMock.Net.Matchers.SystemTextJsonPath?includePreReleases=true&label=MyGet)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.Matchers.SystemTextJsonPath)
| &nbsp;&nbsp;**WireMock.Net.OpenApiParser** | [![NuGet Badge WireMock.Net.OpenApiParser](https://img.shields.io/nuget/v/WireMock.Net.OpenApiParser)](https://www.nuget.org/packages/WireMock.Net.OpenApiParser) | [![MyGet Badge WireMock.Net.OpenApiParser](https://img.shields.io/myget/wiremock-net/vpre/WireMock.Net.OpenApiParser?includePreReleases=true&label=MyGet)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.OpenApiParser)
| &nbsp;&nbsp;**WireMock.Net.MimePart** | [![NuGet Badge WireMock.Net.MimePart](https://img.shields.io/nuget/v/WireMock.Net.MimePart)](https://www.nuget.org/packages/WireMock.Net.MimePart) | [![MyGet Badge WireMock.Net.MimePart](https://img.shields.io/myget/wiremock-net/vpre/WireMock.Net.MimePart?includePreReleases=true&label=MyGet)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.MimePart)
| &nbsp;&nbsp;**WireMock.Net.GraphQL** | [![NuGet Badge WireMock.Net.GraphQL](https://img.shields.io/nuget/v/WireMock.Net.GraphQL)](https://www.nuget.org/packages/WireMock.Net.GraphQL) | [![MyGet Badge WireMock.Net.GraphQL](https://img.shields.io/myget/wiremock-net/vpre/WireMock.Net.GraphQL?includePreReleases=true&label=MyGet)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.GraphQL)
@@ -71,11 +73,12 @@ A C# .NET version based on [mock4net](https://github.com/alexvictoor/mock4net) w
| &nbsp;&nbsp;**WireMock.Net.OpenTelemetry** | [![NuGet Badge WireMock.Net.OpenTelemetry](https://img.shields.io/nuget/v/WireMock.Net.OpenTelemetry)](https://www.nuget.org/packages/WireMock.Net.ProtoBuf) | [![MyGet Badge WireMock.Net.OpenTelemetry](https://img.shields.io/myget/wiremock-net/vpre/WireMock.Net.OpenTelemetry?includePreReleases=true&label=MyGet)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.OpenTelemetry)
| | | |
| &nbsp;&nbsp;**WireMock.Net.RestClient** | [![NuGet Badge WireMock.Net.RestClient](https://img.shields.io/nuget/v/WireMock.Net.RestClient)](https://www.nuget.org/packages/WireMock.Net.RestClient) | [![MyGet Badge WireMock.Net.RestClient](https://img.shields.io/myget/wiremock-net/vpre/WireMock.Net.RestClient?includePreReleases=true&label=MyGet)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.RestClient)
| &nbsp;&nbsp;**WireMock.Net.RestClient.AwesomeAssertions** | [![NuGet Badge WireMock.Net.RestClient.AwesomeAssertions](https://img.shields.io/nuget/v/WireMock.Net.RestClient.AwesomeAssertions)](https://www.nuget.org/packages/WireMock.Net.RestClient.AwesomeAssertions) | [![MyGet Badge WireMock.Net.RestClient.AwesomeAssertions](https://img.shields.io/myget/wiremock-net/vpre/WireMock.Net.RestClient.AwesomeAssertions?includePreReleases=true&label=MyGet)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.RestClient.AwesomeAssertions)
| &nbsp;&nbsp;**WireMock.Org.RestClient** | [![NuGet Badge WireMock.Org.RestClient](https://img.shields.io/nuget/v/WireMock.Org.RestClient)](https://www.nuget.org/packages/WireMock.Org.RestClient) | [![MyGet Badge WireMock.Org.RestClient](https://img.shields.io/myget/wiremock-net/vpre/WireMock.Org.RestClient?includePreReleases=true&label=MyGet)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Org.RestClient)
<br />
🔺 **WireMock.Net.Minimal** does not include *WireMock.Net.MimePart*, *WireMock.Net.GraphQL*, *WireMock.Net.ProtoBuf* and *WireMock.Net.OpenTelemetry*.
🔺 **WireMock.Net.Minimal** does not include *WireMock.Net.MimePart*, *WireMock.Net.GraphQL*, *WireMock.Net.ProtoBuf*, *WireMock.Net.OpenTelemetry* and *WireMock.Net.Matchers.SystemTextJsonPath*.
---
+30 -33
View File
@@ -1,4 +1,3 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 18
VisualStudioVersion = 18.0.11205.157
@@ -38,8 +37,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Client", "examples\WireMock.Net.Client\WireMock.Net.Client.csproj", "{74D91AD0-D96D-4FD2-AEC5-CC49D38346C0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.Service", "examples\Wiremock.Net.Service\WireMock.Net.Service.csproj", "{7F0B2446-0363-4720-AF46-F47F83B557DC}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Console.RequestLogTest", "examples\WireMock.Net.Console.RequestLogTest\WireMock.Net.Console.RequestLogTest.csproj", "{A9D039B9-7509-4CF1-9EFD-87EB82998575}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.OpenApiParser.ConsoleApp", "examples\WireMock.Net.OpenApiParser.ConsoleApp\WireMock.Net.OpenApiParser.ConsoleApp.csproj", "{5C09FB93-1535-4F92-AF26-21E8A061EE4A}"
@@ -76,10 +73,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Testcontainers
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.TestcontainersExample", "examples\WireMock.Net.TestcontainersExample\WireMock.Net.TestcontainersExample.csproj", "{56A38798-C48B-4A4A-B805-071E05C02CE1}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{0147029F-FA4A-44B3-B79A-3C3574054EE4}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MultipartUploader", "tools\MultipartUploader\MultipartUploader.csproj", "{07C30227-ADEC-4BDE-8CDC-849D85A690BB}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Console.NET8", "examples\WireMock.Net.Console.NET8\WireMock.Net.Console.NET8.csproj", "{1EA72C0F-92E9-486B-8FFE-53F992BFC4AA}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMockAzureQueueProxy", "examples\WireMockAzureQueueProxy\WireMockAzureQueueProxy.csproj", "{7FC0B409-2682-40EE-B3B9-3930D6769D01}"
@@ -156,6 +149,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.TestWebApplica
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.RestClient.AwesomeAssertions", "src\WireMock.Net.RestClient.AwesomeAssertions\WireMock.Net.RestClient.AwesomeAssertions.csproj", "{F4B2B967-98D7-4D93-9A5C-5EF7B84B941A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.Matchers.SystemTextJsonPath", "src\WireMock.Net.Matchers.SystemTextJsonPath\WireMock.Net.Matchers.SystemTextJsonPath.csproj", "{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.Service", "examples\WireMock.Net.Service\WireMock.Net.Service.csproj", "{7F0B2446-0363-4720-AF46-F47F83B557DC}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -238,18 +235,6 @@ Global
{74D91AD0-D96D-4FD2-AEC5-CC49D38346C0}.Release|x64.Build.0 = Release|Any CPU
{74D91AD0-D96D-4FD2-AEC5-CC49D38346C0}.Release|x86.ActiveCfg = Release|Any CPU
{74D91AD0-D96D-4FD2-AEC5-CC49D38346C0}.Release|x86.Build.0 = Release|Any CPU
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Debug|x64.ActiveCfg = Debug|Any CPU
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Debug|x64.Build.0 = Debug|Any CPU
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Debug|x86.ActiveCfg = Debug|Any CPU
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Debug|x86.Build.0 = Debug|Any CPU
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Release|Any CPU.Build.0 = Release|Any CPU
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Release|x64.ActiveCfg = Release|Any CPU
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Release|x64.Build.0 = Release|Any CPU
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Release|x86.ActiveCfg = Release|Any CPU
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Release|x86.Build.0 = Release|Any CPU
{A9D039B9-7509-4CF1-9EFD-87EB82998575}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A9D039B9-7509-4CF1-9EFD-87EB82998575}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A9D039B9-7509-4CF1-9EFD-87EB82998575}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -394,18 +379,6 @@ Global
{56A38798-C48B-4A4A-B805-071E05C02CE1}.Release|x64.Build.0 = Release|Any CPU
{56A38798-C48B-4A4A-B805-071E05C02CE1}.Release|x86.ActiveCfg = Release|Any CPU
{56A38798-C48B-4A4A-B805-071E05C02CE1}.Release|x86.Build.0 = Release|Any CPU
{07C30227-ADEC-4BDE-8CDC-849D85A690BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{07C30227-ADEC-4BDE-8CDC-849D85A690BB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{07C30227-ADEC-4BDE-8CDC-849D85A690BB}.Debug|x64.ActiveCfg = Debug|Any CPU
{07C30227-ADEC-4BDE-8CDC-849D85A690BB}.Debug|x64.Build.0 = Debug|Any CPU
{07C30227-ADEC-4BDE-8CDC-849D85A690BB}.Debug|x86.ActiveCfg = Debug|Any CPU
{07C30227-ADEC-4BDE-8CDC-849D85A690BB}.Debug|x86.Build.0 = Debug|Any CPU
{07C30227-ADEC-4BDE-8CDC-849D85A690BB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{07C30227-ADEC-4BDE-8CDC-849D85A690BB}.Release|Any CPU.Build.0 = Release|Any CPU
{07C30227-ADEC-4BDE-8CDC-849D85A690BB}.Release|x64.ActiveCfg = Release|Any CPU
{07C30227-ADEC-4BDE-8CDC-849D85A690BB}.Release|x64.Build.0 = Release|Any CPU
{07C30227-ADEC-4BDE-8CDC-849D85A690BB}.Release|x86.ActiveCfg = Release|Any CPU
{07C30227-ADEC-4BDE-8CDC-849D85A690BB}.Release|x86.Build.0 = Release|Any CPU
{1EA72C0F-92E9-486B-8FFE-53F992BFC4AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1EA72C0F-92E9-486B-8FFE-53F992BFC4AA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1EA72C0F-92E9-486B-8FFE-53F992BFC4AA}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -850,6 +823,30 @@ Global
{F4B2B967-98D7-4D93-9A5C-5EF7B84B941A}.Release|x64.Build.0 = Release|Any CPU
{F4B2B967-98D7-4D93-9A5C-5EF7B84B941A}.Release|x86.ActiveCfg = Release|Any CPU
{F4B2B967-98D7-4D93-9A5C-5EF7B84B941A}.Release|x86.Build.0 = Release|Any CPU
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|x64.ActiveCfg = Debug|Any CPU
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|x64.Build.0 = Debug|Any CPU
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|x86.ActiveCfg = Debug|Any CPU
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|x86.Build.0 = Debug|Any CPU
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|Any CPU.Build.0 = Release|Any CPU
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|x64.ActiveCfg = Release|Any CPU
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|x64.Build.0 = Release|Any CPU
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|x86.ActiveCfg = Release|Any CPU
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|x86.Build.0 = Release|Any CPU
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Debug|x64.ActiveCfg = Debug|Any CPU
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Debug|x64.Build.0 = Debug|Any CPU
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Debug|x86.ActiveCfg = Debug|Any CPU
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Debug|x86.Build.0 = Debug|Any CPU
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Release|Any CPU.Build.0 = Release|Any CPU
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Release|x64.ActiveCfg = Release|Any CPU
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Release|x64.Build.0 = Release|Any CPU
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Release|x86.ActiveCfg = Release|Any CPU
{7F0B2446-0363-4720-AF46-F47F83B557DC}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -861,7 +858,6 @@ Global
{B6269AAC-170A-43D5-8B9A-579DED3D9A95} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
{31DC2EF8-C3FE-467D-84BE-FB5D956E612E} = {0BB8B634-407A-4610-A91F-11586990767A}
{74D91AD0-D96D-4FD2-AEC5-CC49D38346C0} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
{7F0B2446-0363-4720-AF46-F47F83B557DC} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
{A9D039B9-7509-4CF1-9EFD-87EB82998575} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
{5C09FB93-1535-4F92-AF26-21E8A061EE4A} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
{B6269AAC-170A-4346-8B9A-579DED3D9A95} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
@@ -876,7 +872,6 @@ Global
{BAA9EC2A-874B-45CE-8E51-A73622DC7F3D} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
{12B016A5-9D8B-4EFE-96C2-CA51BE43367D} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
{56A38798-C48B-4A4A-B805-071E05C02CE1} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
{07C30227-ADEC-4BDE-8CDC-849D85A690BB} = {0147029F-FA4A-44B3-B79A-3C3574054EE4}
{1EA72C0F-92E9-486B-8FFE-53F992BFC4AA} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
{7FC0B409-2682-40EE-B3B9-3930D6769D01} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
{B1580A38-84E7-44BE-8FE7-3EE5031D74A1} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
@@ -914,6 +909,8 @@ Global
{2CE8E3A6-59CC-FE9C-9399-AD54E1FA862B} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
{3B05CC76-C3CB-8667-6B65-3129DFB25681} = {0BB8B634-407A-4610-A91F-11586990767A}
{F4B2B967-98D7-4D93-9A5C-5EF7B84B941A} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
{7F0B2446-0363-4720-AF46-F47F83B557DC} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {DC539027-9852-430C-B19F-FD035D018458}
+7
View File
@@ -1,3 +1,9 @@
parameters:
- name: runWindowsReleaseToMyGet
displayName: Run Windows Release to MyGet
type: boolean
default: false
variables:
Prerelease: 'ci'
buildId: "1$(Build.BuildId)"
@@ -131,6 +137,7 @@ jobs:
- job: Windows_Release_to_MyGet
dependsOn: Windows_Build_Test
condition: and(succeeded(), ${{ parameters.runWindowsReleaseToMyGet }})
pool:
vmImage: 'windows-2025'
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<Sdk Name="Aspire.AppHost.Sdk" Version="13.1.0" />
<Sdk Name="Aspire.AppHost.Sdk" Version="13.4.0" />
<PropertyGroup>
<OutputType>Exe</OutputType>
@@ -18,7 +18,10 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Aspire.Hosting.AppHost" Version="13.1.0" />
<PackageReference Include="Aspire.Hosting.AppHost" Version="13.4.3" />
<!-- CVE-2026-48109 / CVE-2026-92707 -->
<PackageReference Include="MessagePack" Version="2.5.303" />
</ItemGroup>
<ItemGroup>
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<Sdk Name="Aspire.AppHost.Sdk" Version="13.1.0" />
<Sdk Name="Aspire.AppHost.Sdk" Version="13.4.0" />
<PropertyGroup>
<OutputType>Exe</OutputType>
@@ -15,7 +15,10 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Aspire.Hosting.AppHost" Version="13.1.0" />
<PackageReference Include="Aspire.Hosting.AppHost" Version="13.4.3" />
<!-- CVE-2026-48109 / CVE-2026-92707 -->
<PackageReference Include="MessagePack" Version="2.5.303" />
</ItemGroup>
</Project>
@@ -12,11 +12,11 @@
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="8.4.0" />
<PackageReference Include="Microsoft.Extensions.ServiceDiscovery" Version="8.0.0" />
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.8.1" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.8.1" />
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.8.1" />
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.8.1" />
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" Version="1.8.0" />
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.15.3" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.15.3" />
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.15.2" />
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.15.1" />
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" Version="1.15.1" />
</ItemGroup>
</Project>
@@ -12,6 +12,8 @@
<PackageReference Include="Aspire.Hosting.Testing" Version="13.1.0" />
<PackageReference Include="coverlet.collector" Version="6.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageReference Include="System.Net.Http" Version="4.3.4" />
<PackageReference Include="System.Text.RegularExpressions" Version="4.3.1" />
<PackageReference Include="xunit" Version="2.5.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" />
</ItemGroup>
+45 -2
View File
@@ -1,9 +1,7 @@
// Copyright © WireMock.Net
using System;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using RestEase;
using WireMock.Admin.Settings;
@@ -82,6 +80,51 @@ class Program
)
);
mappingBuilder.Given(m => m
.WithRequest(req => req
.WithPath("/testRequestWithQueryParams")
.UsingGet()
.WithParams(p => p.WithParam("param1", pb => pb.WithExactMatcher("value1")))
).WithResponse(rsp => rsp
.WithHeaders(h => h.Add("Content-Type", "application/json"))
.WithStatusCode(200)
.WithBodyAsJson(new
{
status = "ok"
}, true)
)
);
mappingBuilder.Given(m => m
.WithRequest(req => req
.WithPath("/testRequestWithHeaders")
.UsingGet()
.WithHeaders(h => h.WithHeader("Accept", hb => hb.WithExactMatcher("application/json")))
).WithResponse(rsp => rsp
.WithHeaders(h => h.Add("Content-Type", "application/json"))
.WithStatusCode(200)
.WithBodyAsJson(new
{
status = "ok"
}, true)
)
);
mappingBuilder.Given(m => m
.WithRequest(req => req
.WithPath("/testRequestWithCookie")
.UsingGet()
.WithCookies(c => c.WithCookie("cookie1", cb => cb.WithExactMatcher("cookievalue1")))
).WithResponse(rsp => rsp
.WithHeaders(h => h.Add("Content-Type", "application/json"))
.WithStatusCode(200)
.WithBodyAsJson(new
{
status = "ok"
}, true)
)
);
var result = await mappingBuilder.BuildAndPostAsync().ConfigureAwait(false);
Console.WriteLine($"result = {JsonConvert.SerializeObject(result)}");
@@ -9,9 +9,9 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Google.Protobuf" Version="3.25.1" />
<PackageReference Include="Grpc.Net.Client" Version="2.60.0" />
<PackageReference Include="Grpc.Tools" Version="2.60.0">
<PackageReference Include="Google.Protobuf" Version="3.33.5" />
<PackageReference Include="Grpc.Net.Client" Version="2.76.0" />
<PackageReference Include="Grpc.Tools" Version="2.78.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
@@ -9,7 +9,7 @@ namespace WireMock.Net.Console.MimePart;
static class Program
{
private static readonly ILoggerRepository LogRepository = LogManager.GetRepository(Assembly.GetEntryAssembly());
private static readonly ILoggerRepository LogRepository = LogManager.GetRepository(Assembly.GetEntryAssembly()!);
private static readonly ILog Log = LogManager.GetLogger(typeof(Program));
static async Task Main(params string[] args)
@@ -16,7 +16,7 @@
<ItemGroup>
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
<PackageReference Include="log4net" Version="2.0.15" />
<PackageReference Include="log4net" Version="3.3.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
</ItemGroup>
@@ -1,6 +1,5 @@
// Copyright © WireMock.Net
using System.IO;
using WireMock.Logging;
using WireMock.Server;
using WireMock.Settings;
+44 -7
View File
@@ -1,13 +1,9 @@
// Copyright © WireMock.Net
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Scriban.Runtime;
using WireMock.Logging;
using WireMock.Matchers;
using WireMock.Models;
@@ -288,7 +284,34 @@ namespace WireMock.Net.ConsoleApplication
var todos = new Dictionary<int, Todo>();
var server = WireMockServer.Start();
var server = WireMockServer.Start(new WireMockServerSettings
{
StartAdminInterface = true,
Logger = new WireMockConsoleLogger(),
Port = 9091
});
server
.WhenRequest(r => r
.WithPath("/test")
.UsingAnyMethod()
)
.ThenRespondWith(r => r
.WithStatusCode(HttpStatusCode.OK)
.WithBody("OK")
);
server
.WhenRequest(r => r
.WithPath("/Content-Length")
.UsingAnyMethod()
)
.ThenRespondWith(r => r
.WithStatusCode(HttpStatusCode.OK)
.WithHeader("Content-Length", "42")
);
System.Console.ReadLine();
//server
// .Given(Request.Create()
@@ -387,12 +410,26 @@ namespace WireMock.Net.ConsoleApplication
// handlebarsContext.RegisterHelper(transformer.Name, transformer.Render); TODO
},
ScribanRegistrationCallback = (scriptObject, fileSystemHandler) =>
{
var globals = (ScriptObject)scriptObject;
globals.Import("greet", new Func<string, string>(name => $"Hello {name}"));
},
// Uncomment below if you want to use the CustomFileSystemFileHandler
// FileSystemHandler = new CustomFileSystemFileHandler()
});
System.Console.WriteLine("WireMockServer listening at {0}", string.Join(",", server.Urls));
server.SetBasicAuthentication("a", "b");
server
.Given(Request.Create().WithPath("/scriban/greet").UsingGet())
.RespondWith(Response.Create()
.WithBody("{{ greet 'WireMock.Net' }}")
.WithTransformer(TransformerType.Scriban)
);
//server.SetAzureADAuthentication(Environment.GetEnvironmentVariable("WIREMOCK_AAD_TENANT")!, "api://e083d51a-01a6-446c-8ad5-0c5c7f002208");
//var http = new HttpClient();
@@ -1097,7 +1134,7 @@ namespace WireMock.Net.ConsoleApplication
{
BodyData = new BodyData
{
BodyAsString = "random200or505:" + code + ", HeadersFromRequest = " + string.Join(",", request.Headers),
BodyAsString = "random200or505:" + code + ", HeadersFromRequest = " + request.Headers != null ? string.Join(",", request.Headers!.Select(h => $"{h.Key}:{h.Value}")) : "none",
DetectedBodyType = BodyType.String,
},
StatusCode = code
@@ -1,8 +1,6 @@
// Copyright © WireMock.Net
using System.IO;
using System.Reflection;
using System.Threading.Tasks;
using log4net;
using log4net.Config;
using log4net.Repository;
@@ -12,8 +10,7 @@ namespace WireMock.Net.Console.NET8;
static class Program
{
private static readonly ILoggerRepository LogRepository = LogManager.GetRepository(Assembly.GetEntryAssembly());
private static readonly ILog Log = LogManager.GetLogger(typeof(Program));
private static readonly ILoggerRepository LogRepository = LogManager.GetRepository(Assembly.GetEntryAssembly()!);
static async Task Main(params string[] args)
{
@@ -18,7 +18,7 @@
<ItemGroup>
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
<PackageReference Include="log4net" Version="2.0.15" />
<PackageReference Include="log4net" Version="3.3.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
</ItemGroup>
@@ -1,9 +1,5 @@
// Copyright © WireMock.Net
using System;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using WireMock.Logging;
using WireMock.Net.StandAlone;
using WireMock.RequestBuilders;
@@ -16,7 +16,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="WireMock.Net" Version="1.25.0" />
<PackageReference Include="WireMock.Net" Version="2.15.0" />
</ItemGroup>
</Project>
@@ -1,7 +1,5 @@
// Copyright © WireMock.Net
using System;
using System.IO;
using WireMock.RequestBuilders;
using WireMock.ResponseBuilders;
@@ -6,18 +6,13 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.OpenApi.YamlReader" Version="2.3.0" />
<ProjectReference Include="..\..\src\WireMock.Net.Abstractions\WireMock.Net.Abstractions.csproj" />
<ProjectReference Include="..\..\src\WireMock.Net.OpenApiParser\WireMock.Net.OpenApiParser.csproj" />
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
</ItemGroup>
<ItemGroup Condition="'$(Configuration)' == 'Release'">
<PackageReference Include="Microsoft.OpenApi" Version="2.3.0" PrivateAssets="All" />
<!--<PackageReference Include="Microsoft.OpenApi.YamlReader" Version="2.3.0" PrivateAssets="All" />
<PackageReference Include="System.Text.Json" Version="8.0.5" />
<PackageReference Include="SharpYaml" Version="2.1.3" />-->
<PackageReference Include="Microsoft.OpenApi" Version="3.10.2" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
@@ -8,9 +8,9 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="OpenTelemetry.Exporter.Console" Version="1.14.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.14.0" />
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.14.0" />
<PackageReference Include="OpenTelemetry.Exporter.Console" Version="1.15.3" />
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.15.1" />
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.15.3" />
</ItemGroup>
<ItemGroup>
@@ -32,13 +32,10 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<StartupObject>Wiremock.Net.Service.Program</StartupObject>
</PropertyGroup>
<ItemGroup>
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="SimMetrics.Net, Version=1.0.5.0, Culture=neutral, PublicKeyToken=c58dc06d59f3391b, processorArchitecture=MSIL">
<HintPath>..\..\packages\SimMetrics.Net.1.0.5\lib\net45\SimMetrics.Net.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.Configuration.Install" />
@@ -88,13 +85,13 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="log4net">
<Version>3.3.0</Version>
<Version>3.3.2</Version>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel.Core">
<Version>2.3.9</Version>
<Version>2.3.11</Version>
</PackageReference>
<PackageReference Include="WireMock.Net">
<Version>1.25.0</Version>
<Version>2.12.0</Version>
</PackageReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="log4net" version="2.0.15" targetFramework="net48" />
<package id="log4net" version="3.3.0" targetFramework="net48" />
<package id="Microsoft.AspNet.WebApi.Client" version="5.2.3" targetFramework="net452" />
<package id="Microsoft.AspNet.WebApi.Core" version="5.2.3" targetFramework="net452" />
<package id="Microsoft.AspNet.WebApi.Owin" version="5.2.3" targetFramework="net452" />
@@ -246,8 +246,8 @@ internal class Program
throw new InvalidOperationException($"The {nameof(TestcontainersSettings.OS.DockerEndpointAuthConfig)} is null. Check if Docker is started.");
}
using var dockerClientConfig = TestcontainersSettings.OS.DockerEndpointAuthConfig.GetDockerClientConfiguration();
using var dockerClient = dockerClientConfig.CreateClient();
var dockerClientBuilder = TestcontainersSettings.OS.DockerEndpointAuthConfig.GetDockerClientBuilder();
using var dockerClient = dockerClientBuilder.Build();
var version = await dockerClient.System.GetVersionAsync();
return version.Os.IndexOf("Windows", StringComparison.OrdinalIgnoreCase) >= 0 ? OSPlatform.Windows : OSPlatform.Linux;
@@ -1,29 +1,18 @@
// Copyright © WireMock.Net
using Microsoft.Extensions.Hosting;
using System.Threading;
using System.Threading.Tasks;
namespace WireMock.Net.WebApplication;
public class App : IHostedService
public class App(IWireMockService service) : IHostedService
{
private readonly IWireMockService _service;
public App(IWireMockService service)
{
_service = service;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_service.Start();
service.Start();
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
_service.Stop();
service.Stop();
return Task.CompletedTask;
}
}
@@ -1,9 +1,5 @@
// Copyright © WireMock.Net
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using WireMock.Settings;
namespace WireMock.Net.WebApplication;
@@ -11,11 +11,10 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\WireMock.Net.Abstractions\WireMock.Net.Abstractions.csproj" />
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
</ItemGroup>
@@ -1,7 +1,5 @@
// Copyright © WireMock.Net
using System;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using WireMock.Admin.Requests;
@@ -15,7 +15,7 @@
"WireMockServerSettings": {
"StartAdminInterface": true,
"Urls": [
"http://localhost"
"http://localhost:0"
]
}
}
@@ -1,12 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<!--
<!--
Configure your application settings in appsettings.json. Learn more at http://go.microsoft.com/fwlink/?LinkId=786380
-->
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
</handlers>
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false" />
</system.webServer>
-->
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
</handlers>
<httpProtocol>
<customHeaders>
<add name="X-Frame-Options" value="SAMEORIGIN" />
</customHeaders>
</httpProtocol>
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false" />
</system.webServer>
</configuration>
@@ -55,12 +55,17 @@ public class MappingModel
/// In case the value is null state will not be changed.
/// </summary>
public string? SetStateTo { get; set; }
/// <summary>
/// The number of times this match should be matched before the state will be changed to the specified one.
/// </summary>
public int? TimesInSameState { get; set; }
/// <summary>
/// Value to determine if the mapping is disabled. Defaults to <c>null</c> (not disabled).
/// </summary>
public bool? IsDisabled { get; set; }
/// <summary>
/// The request model.
/// </summary>
@@ -100,7 +105,7 @@ public class MappingModel
/// </summary>
public object? Data { get; set; }
/// <summary>
/// <summary>
/// The probability when this request should be matched. Value is between 0 and 1. [Optional]
/// </summary>
public double? Probability { get; set; }
@@ -1,5 +1,7 @@
// Copyright © WireMock.Net
using WireMock.Matchers.Request;
namespace WireMock.Admin.Mappings;
/// <summary>
@@ -61,9 +63,15 @@ public class RequestModel
/// Gets or sets the Params.
/// </summary>
public IList<ParamModel>? Params { get; set; }
/// <summary>
/// Gets or sets the body.
/// </summary>
public BodyModel? Body { get; set; }
/// <summary>
/// Type of the request matcher to return an immediate mismatch during mapping processing.
/// Optional.
/// </summary>
public RequestMatcherType? EarlyMatcherType { get; set; }
}
@@ -0,0 +1,34 @@
// Copyright © WireMock.Net
namespace WireMock.Admin.Requests;
/// <summary>
/// NearMiss Model: a mapping which did not (fully) match a request.
/// </summary>
public class NearMissModel
{
/// <summary>
/// The unique identifier of the log entry for the request. [Optional]
/// </summary>
public Guid? LogEntryGuid { get; set; }
/// <summary>
/// The request.
/// </summary>
public LogRequestModel? Request { get; set; }
/// <summary>
/// The unique identifier of the mapping.
/// </summary>
public Guid MappingGuid { get; set; }
/// <summary>
/// The title of the mapping.
/// </summary>
public string? MappingTitle { get; set; }
/// <summary>
/// The request match result, containing the score per matcher.
/// </summary>
public LogRequestMatchModel? RequestMatchResult { get; set; }
}
@@ -0,0 +1,14 @@
// Copyright © WireMock.Net
namespace WireMock.Admin.Requests;
/// <summary>
/// RequestCount Model
/// </summary>
public class RequestCountModel
{
/// <summary>
/// The number of requests which match the criteria.
/// </summary>
public int Count { get; set; }
}
@@ -0,0 +1,39 @@
// Copyright © WireMock.Net
namespace WireMock.Admin.Mappings;
public partial class ArrayMatcherModelBuilder
{
public ArrayMatcherModelBuilder WithExactMatcher(object pattern, bool rejectOnMatch = false, bool ignoreCase = false)
{
return WithMatcher("ExactMatcher", pattern, rejectOnMatch, ignoreCase);
}
public ArrayMatcherModelBuilder WithWildcardMatcher(object pattern, bool rejectOnMatch = false, bool ignoreCase = false)
{
return WithMatcher("WildcardMatcher", pattern, rejectOnMatch, ignoreCase);
}
public ArrayMatcherModelBuilder WithRegexMatcher(object pattern, bool rejectOnMatch = false, bool ignoreCase = false)
{
return WithMatcher("RegexMatcher", pattern, rejectOnMatch, ignoreCase);
}
public ArrayMatcherModelBuilder WithNotNullOrEmptyMatcher(bool rejectOnMatch = false)
{
return Add(mb => mb
.WithName("NotNullOrEmptyMatcher")
.WithRejectOnMatch(rejectOnMatch)
);
}
private ArrayMatcherModelBuilder WithMatcher(string name, object pattern, bool rejectOnMatch, bool ignoreCase = false)
{
return Add(mb => mb
.WithName(name)
.WithPattern(pattern)
.WithRejectOnMatch(rejectOnMatch)
.WithIgnoreCase(ignoreCase)
);
}
}
@@ -0,0 +1,19 @@
// Copyright © WireMock.Net
using System;
using System.Collections.Generic;
using System.Text;
namespace WireMock.Admin.Mappings;
public partial class IListCookieModelBuilder
{
public IListCookieModelBuilder WithCookie(string name, Action<IListMatcherModelBuilder> action, bool rejectOnMatch = false)
{
return Add(cookieBuilder => cookieBuilder
.WithName(name)
.WithRejectOnMatch(rejectOnMatch)
.WithMatchers(matchersBuilder => action(matchersBuilder))
);
}
}
@@ -0,0 +1,19 @@
// Copyright © WireMock.Net
using System;
using System.Collections.Generic;
using System.Text;
namespace WireMock.Admin.Mappings;
public partial class IListHeaderModelBuilder
{
public IListHeaderModelBuilder WithHeader(string name, Action<IListMatcherModelBuilder> action, bool rejectOnMatch = false)
{
return Add(headerBuilder => headerBuilder
.WithName(name)
.WithRejectOnMatch(rejectOnMatch)
.WithMatchers(matchersBuilder => action(matchersBuilder))
);
}
}
@@ -0,0 +1,39 @@
// Copyright © WireMock.Net
namespace WireMock.Admin.Mappings;
public partial class IListMatcherModelBuilder
{
public IListMatcherModelBuilder WithExactMatcher(object pattern, bool rejectOnMatch = false, bool ignoreCase = false)
{
return WithMatcher("ExactMatcher", pattern, rejectOnMatch, ignoreCase);
}
public IListMatcherModelBuilder WithWildcardMatcher(object pattern, bool rejectOnMatch = false, bool ignoreCase = false)
{
return WithMatcher("WildcardMatcher", pattern, rejectOnMatch, ignoreCase);
}
public IListMatcherModelBuilder WithRegexMatcher(object pattern, bool rejectOnMatch = false, bool ignoreCase = false)
{
return WithMatcher("RegexMatcher", pattern, rejectOnMatch, ignoreCase);
}
public IListMatcherModelBuilder WithNotNullOrEmptyMatcher(bool rejectOnMatch = false)
{
return Add(mb => mb
.WithName("NotNullOrEmptyMatcher")
.WithRejectOnMatch(rejectOnMatch)
);
}
private IListMatcherModelBuilder WithMatcher(string name, object pattern, bool rejectOnMatch, bool ignoreCase = false)
{
return Add(mb => mb
.WithName(name)
.WithPattern(pattern)
.WithRejectOnMatch(rejectOnMatch)
.WithIgnoreCase(ignoreCase)
);
}
}
@@ -0,0 +1,17 @@
// Copyright © WireMock.Net
using System;
namespace WireMock.Admin.Mappings;
public partial class IListParamModelBuilder
{
public IListParamModelBuilder WithParam(string name, Action<ArrayMatcherModelBuilder> action, bool rejectOnMatch = false)
{
return Add(paramBuilder => paramBuilder
.WithName(name)
.WithRejectOnMatch(rejectOnMatch)
.WithMatchers(matchersBuilder => action(matchersBuilder))
);
}
}
@@ -0,0 +1,24 @@
// Copyright © WireMock.Net
using System.Diagnostics.CodeAnalysis;
namespace WireMock.Handlers;
public interface IScenarioStateStore
{
bool TryGet(string name, [NotNullWhen(true)] out ScenarioState? state);
IReadOnlyList<ScenarioState> GetAll();
bool ContainsKey(string name);
bool TryAdd(string name, ScenarioState scenarioState);
ScenarioState AddOrUpdate(string name, Func<string, ScenarioState> addFactory, Func<string, ScenarioState, ScenarioState> updateFactory);
ScenarioState? Update(string name, Action<ScenarioState> updateAction);
bool TryRemove(string name);
void Clear();
}
@@ -32,7 +32,7 @@ public interface IRequestMessage
string? ProxyUrl { get; set; }
/// <summary>
/// Gets the DateTime.
/// The DateTime when this request was done.
/// </summary>
DateTime DateTime { get; }
@@ -52,7 +52,7 @@ public interface IResponseMessage
object? StatusCode { get; set; }
/// <summary>
/// Gets the DateTime.
/// The DateTime when this response was sent.
/// </summary>
DateTime DateTime { get; }
@@ -7,6 +7,11 @@ namespace WireMock.Matchers.Request;
/// </summary>
public interface IRequestMatcher
{
/// <summary>
/// Gets the request matcher's type.
/// </summary>
public RequestMatcherType Type { get; }
/// <summary>
/// Determines whether the specified RequestMessage is match.
/// </summary>
@@ -10,7 +10,7 @@ public class MatchDetail
/// <summary>
/// Gets or sets the type of the matcher.
/// </summary>
public required Type MatcherType { get; set; }
public required string MatcherType { get; set; }
/// <summary>
/// Gets or sets the type of the matcher.
@@ -0,0 +1,34 @@
// Copyright © WireMock.Net
namespace WireMock.Matchers.Request;
/// <summary>
/// A mapping which did not (fully) match a request, together with the match result explaining why.
/// </summary>
public class NearMiss
{
/// <summary>
/// The request which was evaluated against the mapping.
/// </summary>
public required IRequestMessage RequestMessage { get; init; }
/// <summary>
/// The unique identifier of the log entry for the request. [Optional]
/// </summary>
public Guid? LogEntryGuid { get; init; }
/// <summary>
/// The unique identifier of the mapping.
/// </summary>
public required Guid MappingGuid { get; init; }
/// <summary>
/// The title of the mapping. [Optional]
/// </summary>
public string? MappingTitle { get; init; }
/// <summary>
/// The match result, containing the score per matcher.
/// </summary>
public required IRequestMatchResult RequestMatchResult { get; init; }
}
@@ -0,0 +1,84 @@
// Copyright © WireMock.Net
namespace WireMock.Matchers.Request;
/// <summary>
/// List of predefined request matcher types.
/// </summary>
public enum RequestMatcherType
{
/// <summary>
/// RequestMessageBodyMatcher
/// </summary>
Body = 0,
/// <summary>
/// RequestMessageBodyMatcher{T}
/// </summary>
BodyOfT = 1,
/// <summary>
/// RequestMessageClientIPMatcher
/// </summary>
ClientIP = 2,
/// <summary>
/// RequestMessageCookieMatcher
/// </summary>
Cookie = 3,
/// <summary>
/// RequestMessageGraphQLMatcher
/// </summary>
GraphQL = 4,
/// <summary>
/// RequestMessageHeaderMatcher
/// </summary>
Header = 5,
/// <summary>
/// RequestMessageHttpVersionMatcher
/// </summary>
HttpVersion = 6,
/// <summary>
/// RequestMessageMethodMatcher
/// </summary>
Method = 7,
/// <summary>
/// RequestMessageMultiPartMatcher
/// </summary>
MultiPart = 8,
/// <summary>
/// RequestMessageParamMatcher
/// </summary>
Param = 9,
/// <summary>
/// RequestMessagePathMatcher
/// </summary>
Path = 10,
/// <summary>
/// RequestMessageProtoBufMatcher
/// </summary>
ProtoBuf = 11,
/// <summary>
/// RequestMessageScenarioAndStateMatcher
/// </summary>
ScenarioAndState = 12,
/// <summary>
/// RequestMessageUrlMatcher
/// </summary>
Url = 13,
/// <summary>
/// RequestMessageCompositeMatcher
/// </summary>
Composite = 14
}
@@ -46,7 +46,7 @@ public interface IBodyData
/// <summary>
/// The body as Form UrlEncoded dictionary.
/// </summary>
IDictionary<string, string>? BodyAsFormUrlEncoded { get; set; }
IDictionary<string, WireMockList<string>>? BodyAsFormUrlEncoded { get; set; }
/// <summary>
/// The detected body type (detection based on body content).
@@ -31,4 +31,4 @@ public class ScenarioState
/// Gets or sets the state counter.
/// </summary>
public int Counter { get; set; }
}
}
@@ -33,12 +33,6 @@ public interface IWireMockServer : IDisposable
/// </summary>
IReadOnlyList<MappingModel> MappingModels { get; }
// <summary>
// Gets the mappings.
// </summary>
//[PublicAPI]
//IEnumerable<IMapping> Mappings { get; }
/// <summary>
/// Gets the ports.
/// </summary>
@@ -69,8 +63,6 @@ public interface IWireMockServer : IDisposable
/// </summary>
string? Provider { get; }
//ConcurrentDictionary<string, ScenarioState> Scenarios { get; }
/// <summary>
/// Occurs when [log entries changed].
/// </summary>
@@ -115,7 +107,38 @@ public interface IWireMockServer : IDisposable
/// <returns>The <see cref="IReadOnlyList{ILogEntry}"/>.</returns>
IReadOnlyList<ILogEntry> FindLogEntries(params IRequestMatcher[] matchers);
// IRespondWithAProvider Given(IRequestMatcher requestMatcher, bool saveToFile = false);
/// <summary>
/// Get the log-entries for the requests which did not fully match any mapping.
/// This includes the requests which were served by a partial match when AllowPartialMapping is enabled.
/// </summary>
/// <returns>The <see cref="IReadOnlyList{ILogEntry}"/>.</returns>
IReadOnlyList<ILogEntry> FindUnmatchedLogEntries();
/// <summary>
/// Find the mappings which partially match the request (the near misses), ordered by best match first.
/// Perfect matches and mappings which do not match at all are excluded.
/// Use this to find out why a request did not match the expected mapping.
/// </summary>
/// <param name="requestMessage">The request to evaluate.</param>
/// <param name="maxResults">The maximum number of near misses to return. Default is 3.</param>
/// <returns>The <see cref="IReadOnlyList{NearMiss}"/>.</returns>
IReadOnlyList<NearMiss> FindNearMisses(IRequestMessage requestMessage, int maxResults = 3);
/// <summary>
/// Find the mappings which partially match the request of a log-entry (the near misses), ordered by best match first.
/// Perfect matches and mappings which do not match at all are excluded.
/// </summary>
/// <param name="logEntry">The log-entry for which to evaluate the request.</param>
/// <param name="maxResults">The maximum number of near misses to return. Default is 3.</param>
/// <returns>The <see cref="IReadOnlyList{NearMiss}"/>.</returns>
IReadOnlyList<NearMiss> FindNearMisses(ILogEntry logEntry, int maxResults = 3);
/// <summary>
/// Find the near misses for all requests which did not fully match any mapping.
/// </summary>
/// <param name="maxResultsPerRequest">The maximum number of near misses to return per request. Default is 3.</param>
/// <returns>The <see cref="IReadOnlyList{NearMiss}"/>.</returns>
IReadOnlyList<NearMiss> FindNearMissesForUnmatchedRequests(int maxResultsPerRequest = 3);
/// <summary>
/// Reads a static mapping file and adds or updates a single mapping.
@@ -1,11 +1,11 @@
// Copyright © WireMock.Net
using System.Linq;
namespace WireMock.Types;
/// <summary>
/// A special List which overrides the ToString() to return first value.
/// A special List which overrides the ToString() to return first value in case of a single element.
/// Else it will return a comma separated list of all values.
/// If null or empty, it will return an empty string.
/// </summary>
/// <typeparam name="T">The generic type</typeparam>
/// <seealso cref="List{T}" />
@@ -46,6 +46,98 @@ public class WireMockList<T> : List<T>
/// <param name="values">The values to set.</param>
public static implicit operator WireMockList<T>(T[] values) => new(values);
/// <summary>
/// Operator for equality comparison from WireMockList to T
/// </summary>
public static bool operator ==(WireMockList<T>? left, T? right)
{
if (ReferenceEquals(left, right))
{
return true;
}
if (left?.Count == 1 && Equals(left[0], right))
{
return true;
}
return false;
}
/// <summary>
/// Operator for equality comparison from T to WireMockList
/// </summary>
public static bool operator ==(T? left, WireMockList<T>? right)
{
if (ReferenceEquals(left, right))
{
return true;
}
if (right?.Count == 1 && Equals(left, right[0]))
{
return true;
}
return false;
}
/// <summary>
/// Operator for inequality comparison from WireMockList to T
/// </summary>
public static bool operator !=(WireMockList<T>? left, T? right) => !(left == right);
/// <summary>
/// Operator for inequality comparison from T to WireMockList
/// </summary>
public static bool operator !=(T? left, WireMockList<T>? right) => !(left == right);
/// <summary>
/// Determines whether the specified object is equal to the current instance.
/// Two <see cref="WireMockList{T}"/> instances are equal if they contain the same elements in the same order.
/// </summary>
public override bool Equals(object? obj)
{
if (ReferenceEquals(this, obj))
{
return true;
}
if (obj is WireMockList<T> other)
{
if (Count != other.Count)
{
return false;
}
for (var i = 0; i < Count; i++)
{
if (!Equals(this[i], other[i]))
{
return false;
}
}
return true;
}
return false;
}
/// <summary>
/// Returns a hash code for this instance based on its elements.
/// </summary>
public override int GetHashCode()
{
var hashCode = 17;
foreach (var item in this)
{
hashCode = hashCode * 31 + (item?.GetHashCode() ?? 0);
}
return hashCode;
}
/// <summary>
/// Returns a <see cref="string" /> that represents this instance.
/// </summary>
@@ -62,10 +154,10 @@ public class WireMockList<T> : List<T>
return strValue;
}
return this[0]?.ToString();
return this[0]?.ToString() ?? string.Empty;
default:
var strings = this.Select(x => x as string ?? x?.ToString());
var strings = this.Select(x => x as string ?? x?.ToString() ?? string.Empty);
return string.Join(", ", strings);
}
}
@@ -39,7 +39,7 @@
</ItemGroup>-->
<ItemGroup>
<PackageReference Include="FluentBuilder" Version="0.14.0">
<PackageReference Include="FluentBuilder" Version="0.15.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
@@ -45,7 +45,10 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Aspire.Hosting" Version="13.1.0" />
<PackageReference Include="Aspire.Hosting" Version="13.4.3" />
<!-- CVE-2026-48109 / CVE-2026-92707 -->
<PackageReference Include="MessagePack" Version="2.5.303" />
</ItemGroup>
<ItemGroup>
@@ -21,11 +21,6 @@ public class WireMockHealthCheck(WireMockServerResource resource) : IHealthCheck
return HealthCheckResult.Unhealthy("WireMock.Net is not healthy");
}
if (resource.ApiMappingState == WireMockMappingState.NotSubmitted)
{
return HealthCheckResult.Unhealthy("WireMock.Net has not received mappings");
}
return HealthCheckResult.Healthy();
}
@@ -41,4 +36,4 @@ public class WireMockHealthCheck(WireMockServerResource resource) : IHealthCheck
return false;
}
}
}
}
@@ -21,7 +21,13 @@ internal class WireMockLifecycleSubscriber(ILoggerFactory loggerFactory) : IDist
var endpoint = wireMockServerResource.GetEndpoint();
Debug.Assert(endpoint.IsAllocated);
}
});
eventing.Subscribe<ResourceReadyEvent>(async (@event, ct) =>
{
if (@event.Resource is WireMockServerResource wireMockServerResource)
{
await wireMockServerResource.WaitForHealthAsync(ct);
await wireMockServerResource.CallAddProtoDefinitionsAsync(ct);
@@ -31,19 +31,19 @@ public partial class WireMockAssertions
}
[CustomAssertion]
public AndConstraint<WireMockAssertions> WithBodyAsJson(object body, string because = "", params object[] becauseArgs)
public AndConstraint<WireMockAssertions> WithBodyAsJson(object body, IJsonMatcher? jsonMatcher = null, string because = "", params object[] becauseArgs)
{
return WithBodyAsJson(new JsonMatcher(body), because, becauseArgs);
return WithBodyAsJson(jsonMatcher ?? new JsonMatcher(body), because, becauseArgs);
}
[CustomAssertion]
public AndConstraint<WireMockAssertions> WithBodyAsJson(string body, string because = "", params object[] becauseArgs)
public AndConstraint<WireMockAssertions> WithBodyAsJson(string body, IJsonMatcher? jsonMatcher = null, string because = "", params object[] becauseArgs)
{
return WithBodyAsJson(new JsonMatcher(body), because, becauseArgs);
return WithBodyAsJson(jsonMatcher ?? new JsonMatcher(body), because, becauseArgs);
}
[CustomAssertion]
public AndConstraint<WireMockAssertions> WithBodyAsJson(IObjectMatcher matcher, string because = "", params object[] becauseArgs)
public AndConstraint<WireMockAssertions> WithBodyAsJson(IJsonMatcher matcher, string because = "", params object[] becauseArgs)
{
var (filter, condition) = BuildFilterAndCondition(r => r.BodyAsJson, matcher);
@@ -126,15 +126,44 @@ public partial class WireMockAssertions
private static string? FormatBody(object? body)
{
return body switch
if (body == null)
{
null => null,
string str => str,
AnyOf<string, StringPattern>[] stringPatterns => FormatBodies(stringPatterns.Select(p => p.GetPattern())),
byte[] bytes => $"byte[{bytes.Length}] {{...}}",
JToken jToken => jToken.ToString(Formatting.None),
_ => JToken.FromObject(body).ToString(Formatting.None)
};
return null;
}
if (body is string str)
{
return str;
}
if (body is AnyOf<string, StringPattern>[] stringPatterns)
{
return FormatBodies(stringPatterns.Select(p => p.GetPattern()));
}
if (body is byte[] bytes)
{
return $"byte[{bytes.Length}] {{...}}";
}
if (body is JToken jToken)
{
return jToken.ToString(Formatting.None);
}
// System.IO.FileNotFoundException : Could not load file or assembly 'System.Text.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'. The system cannot find the file specified.
var typeName = body.GetType().FullName;
if (typeName == "System.Text.Json.JsonElement")
{
return ((dynamic)body).GetRawText();
}
if (typeName == "System.Text.Json.JsonDocument")
{
return ((dynamic)body).RootElement.GetRawText();
}
return JToken.FromObject(body).ToString(Formatting.None);
}
private static string? FormatBodies(IEnumerable<object?> bodies)
@@ -32,7 +32,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AwesomeAssertions" Version="9.4.0" />
<PackageReference Include="AwesomeAssertions" Version="9.6.0" />
</ItemGroup>
<ItemGroup>
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>WireMock.Net.Routing extends WireMock.Net with modern, minimal-API-style routing for .NET</Description>
<Authors>Gennadii Saltyshchak</Authors>
@@ -25,7 +25,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="JsonConverter.Abstractions" Version="0.8.0" />
<PackageReference Include="JsonConverter.Abstractions" Version="0.13.0" />
</ItemGroup>
<ItemGroup>
@@ -31,19 +31,19 @@ public partial class WireMockAssertions
}
[CustomAssertion]
public AndConstraint<WireMockAssertions> WithBodyAsJson(object body, string because = "", params object[] becauseArgs)
public AndConstraint<WireMockAssertions> WithBodyAsJson(object body, IJsonMatcher? jsonMatcher = null, string because = "", params object[] becauseArgs)
{
return WithBodyAsJson(new JsonMatcher(body), because, becauseArgs);
return WithBodyAsJson(jsonMatcher ?? new JsonMatcher(body), because, becauseArgs);
}
[CustomAssertion]
public AndConstraint<WireMockAssertions> WithBodyAsJson(string body, string because = "", params object[] becauseArgs)
public AndConstraint<WireMockAssertions> WithBodyAsJson(string body, IJsonMatcher? jsonMatcher = null, string because = "", params object[] becauseArgs)
{
return WithBodyAsJson(new JsonMatcher(body), because, becauseArgs);
return WithBodyAsJson(jsonMatcher ?? new JsonMatcher(body), because, becauseArgs);
}
[CustomAssertion]
public AndConstraint<WireMockAssertions> WithBodyAsJson(IObjectMatcher matcher, string because = "", params object[] becauseArgs)
public AndConstraint<WireMockAssertions> WithBodyAsJson(IJsonMatcher matcher, string because = "", params object[] becauseArgs)
{
var (filter, condition) = BuildFilterAndCondition(r => r.BodyAsJson, matcher);
@@ -126,15 +126,44 @@ public partial class WireMockAssertions
private static string? FormatBody(object? body)
{
return body switch
if (body == null)
{
null => null,
string str => str,
AnyOf<string, StringPattern>[] stringPatterns => FormatBodies(stringPatterns.Select(p => p.GetPattern())),
byte[] bytes => $"byte[{bytes.Length}] {{...}}",
JToken jToken => jToken.ToString(Formatting.None),
_ => JToken.FromObject(body).ToString(Formatting.None)
};
return null;
}
if (body is string str)
{
return str;
}
if (body is AnyOf<string, StringPattern>[] stringPatterns)
{
return FormatBodies(stringPatterns.Select(p => p.GetPattern()));
}
if (body is byte[] bytes)
{
return $"byte[{bytes.Length}] {{...}}";
}
if (body is JToken jToken)
{
return jToken.ToString(Formatting.None);
}
// System.IO.FileNotFoundException : Could not load file or assembly 'System.Text.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'. The system cannot find the file specified.
var typeName = body.GetType().FullName;
if (typeName == "System.Text.Json.JsonElement")
{
return ((dynamic)body).GetRawText();
}
if (typeName == "System.Text.Json.JsonDocument")
{
return ((dynamic)body).RootElement.GetRawText();
}
return JToken.FromObject(body).ToString(Formatting.None);
}
private static string? FormatBodies(IEnumerable<object?> bodies)
@@ -70,7 +70,7 @@ public class CSharpCodeMatcher : ICSharpCodeMatcher
/// <inheritdoc />
public string GetCSharpCodeArguments()
{
return $"new {Name}" +
return $"new {typeof(CSharpCodeMatcher).FullName}" +
$"(" +
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
$"{MatchOperator.GetFullyQualifiedEnumValue()}, " +
@@ -0,0 +1,184 @@
// Copyright © WireMock.Net
using System.Text.Json;
using System.Text.Json.Nodes;
using AnyOfTypes;
using Json.Path;
using Stef.Validation;
using WireMock.Extensions;
using WireMock.Models;
using WireMock.Util;
namespace WireMock.Matchers;
/// <summary>
/// SystemTextJsonPathMatcher - behaves the same as JsonPathMatcher but uses System.Text.Json and Json.Path instead of Newtonsoft.Json.
/// </summary>
/// <seealso cref="ISystemTextJsonPathMatcher" />
public class SystemTextJsonPathMatcher : ISystemTextJsonPathMatcher
{
private readonly AnyOf<string, StringPattern>[] _patterns;
/// <inheritdoc />
public MatchBehaviour MatchBehaviour { get; }
/// <inheritdoc />
public object Value { get; }
/// <summary>
/// Initializes a new instance of the <see cref="SystemTextJsonPathMatcher"/> class.
/// </summary>
/// <param name="patterns">The patterns.</param>
public SystemTextJsonPathMatcher(params string[] patterns)
: this(MatchBehaviour.AcceptOnMatch, MatchOperator.Or, patterns.ToAnyOfPatterns())
{
}
/// <summary>
/// Initializes a new instance of the <see cref="SystemTextJsonPathMatcher"/> class.
/// </summary>
/// <param name="patterns">The patterns.</param>
public SystemTextJsonPathMatcher(params AnyOf<string, StringPattern>[] patterns)
: this(MatchBehaviour.AcceptOnMatch, MatchOperator.Or, patterns)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="SystemTextJsonPathMatcher"/> class.
/// </summary>
/// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param>
/// <param name="patterns">The patterns.</param>
public SystemTextJsonPathMatcher(
MatchBehaviour matchBehaviour,
MatchOperator matchOperator = MatchOperator.Or,
params AnyOf<string, StringPattern>[] patterns)
{
_patterns = Guard.NotNull(patterns);
MatchBehaviour = matchBehaviour;
MatchOperator = matchOperator;
Value = patterns;
}
/// <inheritdoc />
public MatchResult IsMatch(string? input)
{
var score = MatchScores.Mismatch;
Exception? exception = null;
if (!string.IsNullOrWhiteSpace(input))
{
try
{
var node = JsonNode.Parse(input!);
score = IsMatchInternal(node);
}
catch (Exception ex)
{
exception = ex;
}
}
return MatchResult.From(Name, MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
}
/// <inheritdoc />
public MatchResult IsMatch(object? input)
{
var score = MatchScores.Mismatch;
Exception? exception = null;
// When input is null or byte[], return Mismatch.
if (input != null && input is not byte[])
{
try
{
JsonNode? node = input switch
{
JsonNode jsonNode => jsonNode,
string str => JsonNode.Parse(str),
_ => JsonNode.Parse(JsonSerializer.Serialize(input))
};
score = IsMatchInternal(node);
}
catch (Exception ex)
{
exception = ex;
}
}
return MatchResult.From(Name, MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
}
/// <inheritdoc />
public AnyOf<string, StringPattern>[] GetPatterns()
{
return _patterns;
}
/// <inheritdoc />
public MatchOperator MatchOperator { get; }
/// <inheritdoc />
public string Name => nameof(SystemTextJsonPathMatcher);
/// <inheritdoc />
public string GetCSharpCodeArguments()
{
return $"new {typeof(SystemTextJsonPathMatcher).FullName}" +
$"(" +
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
$"{MatchOperator.GetFullyQualifiedEnumValue()}, " +
$"{MappingConverterUtils.ToCSharpCodeArguments(_patterns)}" +
$")";
}
private double IsMatchInternal(JsonNode? node)
{
// JsonPath.Net requires the node to be inside an object or array for filter expressions.
// Similar to JsonPathMatcher's ConvertJTokenToJArrayIfNeeded, wrap a plain object in an array
// when it's an object with a single non-array child property.
var evaluationNode = WrapIfNeeded(node);
var values = _patterns
.Select(pattern =>
{
var path = JsonPath.Parse(pattern.GetPattern());
var result = path.Evaluate(evaluationNode);
return result.Matches is { Count: > 0 };
})
.ToArray();
return MatchScores.ToScore(values, MatchOperator);
}
// Mirrors JsonPathMatcher.ConvertJTokenToJArrayIfNeeded:
// If the node is an object with exactly one property whose value is not already an array,
// wrap that value in an array so that filter expressions (e.g. [?(@.x == y)]) can match.
private static JsonNode? WrapIfNeeded(JsonNode? node)
{
if (node is not JsonObject obj)
{
return node;
}
var properties = obj.ToList();
if (properties.Count != 1)
{
return node;
}
var single = properties[0];
if (single.Value is JsonArray)
{
return node;
}
var clonedValue = JsonNode.Parse(single.Value?.ToJsonString() ?? "null");
return new JsonObject
{
[single.Key] = new JsonArray(clonedValue)
};
}
}
@@ -0,0 +1,5 @@
// Copyright © WireMock.Net
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("WireMock.Net.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e138ec44d93acac565953052636eb8d5e7e9f27ddb030590055cd1a0ab2069a5623f1f77ca907d78e0b37066ca0f6d63da7eecc3fcb65b76aa8ebeccf7ebe1d11264b8404cd9b1cbbf2c83f566e033b3e54129f6ef28daffff776ba7aebbc53c0d635ebad8f45f78eb3f7e0459023c218f003416e080f96a1a3c5ffeb56bee9e")]
@@ -0,0 +1,42 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>A SystemTextJsonPathMatcher which can be used to match WireMock.Net Requests using JsonPath.Net.</Description>
<AssemblyTitle>WireMock.Net.Matchers.SystemTextJsonPath</AssemblyTitle>
<Authors>Stef Heyenrath</Authors>
<TargetFrameworks>netstandard2.0;net8.0</TargetFrameworks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageTags>wiremock;matchers;matcher;jsonpath;systemtextjson</PackageTags>
<RootNamespace>WireMock</RootNamespace>
<PackageId>WireMock.Net.Matchers.SystemTextJsonPath</PackageId>
<ProjectGuid>{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}</ProjectGuid>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>../WireMock.Net/WireMock.Net.snk</AssemblyOriginatorKeyFile>
<!--<DelaySign>true</DelaySign>-->
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug - Sonar'">
<CodeAnalysisRuleSet>../WireMock.Net/WireMock.Net.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\WireMock.Net.Shared\WireMock.Net.Shared.csproj" />
</ItemGroup>
<ItemGroup>
<!-- Downgraded from 3.0.2 to 2.2.0 to avoid Open Source Maintenance Fee issues -->
<PackageReference Include="JsonPath.Net" Version="2.2.0" />
</ItemGroup>
</Project>
@@ -1,7 +1,8 @@
// Copyright © WireMock.Net
using System;
using System.Collections.Generic;
using JsonConverter.Abstractions;
using JsonConverter.Newtonsoft.Json;
using Newtonsoft.Json;
using WireMock.Matchers.Helpers;
using WireMock.Models.Mime;
using WireMock.Util;
@@ -15,6 +16,8 @@ public class MimePartMatcher : IMimePartMatcher
{
private readonly IList<(string Name, Func<IMimePartData, MatchResult> func)> _matcherFunctions;
private readonly IJsonConverter _jsonConverter;
/// <inheritdoc />
public string Name => nameof(MimePartMatcher);
@@ -41,7 +44,8 @@ public class MimePartMatcher : IMimePartMatcher
IStringMatcher? contentTypeMatcher,
IStringMatcher? contentDispositionMatcher,
IStringMatcher? contentTransferEncodingMatcher,
IMatcher? contentMatcher
IMatcher? contentMatcher,
IJsonConverter? jsonConverter = null
)
{
MatchBehaviour = matchBehaviour;
@@ -49,6 +53,7 @@ public class MimePartMatcher : IMimePartMatcher
ContentDispositionMatcher = contentDispositionMatcher;
ContentTransferEncodingMatcher = contentTransferEncodingMatcher;
ContentMatcher = contentMatcher;
_jsonConverter = jsonConverter ?? new NewtonsoftJsonConverter();
_matcherFunctions = [];
if (ContentTypeMatcher != null)
@@ -107,7 +112,8 @@ public class MimePartMatcher : IMimePartMatcher
ContentType = GetContentTypeAsString(mimePart.ContentType),
DeserializeJson = true,
ContentEncoding = null, // mimePart.ContentType?.CharsetEncoding.ToString(),
DecompressGZipAndDeflate = true
DecompressGZipAndDeflate = true,
DefaultJsonConverter = _jsonConverter
};
var bodyData = BodyParser.ParseAsync(bodyParserSettings).ConfigureAwait(false).GetAwaiter().GetResult();
@@ -36,7 +36,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Stef.Validation" Version="0.2.0" />
<PackageReference Include="Stef.Validation" Version="0.3.0" />
</ItemGroup>
<ItemGroup Condition="'$(Configuration)' == 'Debug'">
@@ -0,0 +1,14 @@
// Copyright © WireMock.Net
using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNetCore.Http;
namespace WireMock.Extensions;
internal static class HttpContextExtensions
{
internal static bool TryGet<T>(this HttpContext httpContext, [NotNullWhen(true)] out T? value)
{
return httpContext.Items.TryGetValue(nameof(T), out value);
}
}
@@ -0,0 +1,145 @@
// Copyright © WireMock.Net
using System.Collections.Concurrent;
using System.Diagnostics.CodeAnalysis;
using Newtonsoft.Json;
using Stef.Validation;
namespace WireMock.Handlers;
/// <summary>
/// Provides a file-based implementation of <see cref="IScenarioStateStore" /> that persists scenario states to disk and allows concurrent access.
/// </summary>
public class FileBasedScenarioStateStore : IScenarioStateStore
{
private readonly ConcurrentDictionary<string, ScenarioState> _scenarios = new(StringComparer.OrdinalIgnoreCase);
private readonly string _scenariosFolder;
private readonly object _lock = new();
/// <summary>
/// Initializes a new instance of the FileBasedScenarioStateStore class using the specified root folder as the base directory for scenario state storage.
/// </summary>
/// <param name="rootFolder">The root directory under which scenario state data will be stored. Must be a valid file system path.</param>
public FileBasedScenarioStateStore(string rootFolder)
{
Guard.NotNullOrEmpty(rootFolder);
_scenariosFolder = Path.Combine(rootFolder, "__admin", "scenarios");
Directory.CreateDirectory(_scenariosFolder);
LoadScenariosFromDisk();
}
/// <inheritdoc />
public bool TryGet(string name, [NotNullWhen(true)] out ScenarioState? state)
{
return _scenarios.TryGetValue(name, out state);
}
/// <inheritdoc />
public IReadOnlyList<ScenarioState> GetAll()
{
return _scenarios.Values.ToArray();
}
/// <inheritdoc />
public bool ContainsKey(string name)
{
return _scenarios.ContainsKey(name);
}
/// <inheritdoc />
public bool TryAdd(string name, ScenarioState scenarioState)
{
if (_scenarios.TryAdd(name, scenarioState))
{
WriteScenarioToFile(name, scenarioState);
return true;
}
return false;
}
/// <inheritdoc />
public ScenarioState AddOrUpdate(string name, Func<string, ScenarioState> addFactory, Func<string, ScenarioState, ScenarioState> updateFactory)
{
lock (_lock)
{
var result = _scenarios.AddOrUpdate(name, addFactory, updateFactory);
WriteScenarioToFile(name, result);
return result;
}
}
/// <inheritdoc />
public ScenarioState? Update(string name, Action<ScenarioState> updateAction)
{
lock (_lock)
{
if (_scenarios.TryGetValue(name, out var state))
{
updateAction(state);
WriteScenarioToFile(name, state);
return state;
}
return null;
}
}
/// <inheritdoc />
public bool TryRemove(string name)
{
if (_scenarios.TryRemove(name, out _))
{
DeleteScenarioFile(name);
return true;
}
return false;
}
/// <inheritdoc />
public void Clear()
{
_scenarios.Clear();
foreach (var file in Directory.GetFiles(_scenariosFolder, "*.json"))
{
File.Delete(file);
}
}
private string GetScenarioFilePath(string name)
{
var sanitized = string.Concat(name.Select(c => Path.GetInvalidFileNameChars().Contains(c) ? '_' : c));
return Path.Combine(_scenariosFolder, sanitized + ".json");
}
private void WriteScenarioToFile(string name, ScenarioState state)
{
var json = JsonConvert.SerializeObject(state, Formatting.Indented);
File.WriteAllText(GetScenarioFilePath(name), json);
}
private void DeleteScenarioFile(string name)
{
var path = GetScenarioFilePath(name);
if (File.Exists(path))
{
File.Delete(path);
}
}
private void LoadScenariosFromDisk()
{
foreach (var file in Directory.GetFiles(_scenariosFolder, "*.json"))
{
var json = File.ReadAllText(file);
var state = JsonConvert.DeserializeObject<ScenarioState>(json);
if (state != null)
{
_scenarios.TryAdd(state.Name, state);
}
}
}
}
@@ -1,8 +1,7 @@
// Copyright © WireMock.Net
using System.Linq;
using System.Net;
using System.Net.Http;
using JsonConverter.Abstractions;
using WireMock.Util;
namespace WireMock.Http;
@@ -15,7 +14,8 @@ internal static class HttpResponseMessageHelper
Uri originalUri,
bool deserializeJson,
bool decompressGzipAndDeflate,
bool deserializeFormUrlEncoded)
bool deserializeFormUrlEncoded,
IJsonConverter jsonConverter)
{
var responseMessage = new ResponseMessage { StatusCode = (int)httpResponseMessage.StatusCode };
@@ -45,7 +45,8 @@ internal static class HttpResponseMessageHelper
DeserializeJson = deserializeJson,
ContentEncoding = contentEncodingHeader?.FirstOrDefault(),
DecompressGZipAndDeflate = decompressGzipAndDeflate,
DeserializeFormUrlEncoded = deserializeFormUrlEncoded
DeserializeFormUrlEncoded = deserializeFormUrlEncoded,
DefaultJsonConverter = jsonConverter
};
responseMessage.BodyData = await BodyParser.ParseAsync(bodyParserSettings).ConfigureAwait(false);
}
@@ -0,0 +1,16 @@
// Copyright © WireMock.Net
using System.Net;
namespace WireMock;
internal interface IResponseMessageBuilder
{
ResponseMessage Create(HttpStatusCode statusCode, string? status, Guid? guid = null);
ResponseMessage Create(int statusCode, string? status, Guid? guid = null);
ResponseMessage Create(int statusCode, string? status, string? error, Guid? guid = null);
ResponseMessage Create(HttpStatusCode statusCode);
}
@@ -0,0 +1,38 @@
// Copyright © WireMock.Net
using Microsoft.Extensions.Logging;
namespace WireMock.Logging;
internal sealed class WireMockAspNetCoreLogger(IWireMockLogger logger, string categoryName) : ILogger
{
public IDisposable? BeginScope<TState>(TState state) where TState : notnull => null;
public bool IsEnabled(LogLevel logLevel) => logLevel >= LogLevel.Warning;
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
{
if (!IsEnabled(logLevel))
{
return;
}
var message = formatter(state, exception);
if (exception != null)
{
message = $"{message} | Exception: {exception}";
}
switch (logLevel)
{
case LogLevel.Warning:
logger.Warn("[{0}] {1}", categoryName, message);
break;
default:
logger.Error("[{0}] {1}", categoryName, message);
break;
}
}
}
@@ -0,0 +1,19 @@
// Copyright © WireMock.Net
using Microsoft.Extensions.Logging;
namespace WireMock.Logging;
internal sealed class WireMockAspNetCoreLoggerProvider : ILoggerProvider
{
private readonly IWireMockLogger _logger;
public WireMockAspNetCoreLoggerProvider(IWireMockLogger logger)
{
_logger = logger;
}
public ILogger CreateLogger(string categoryName) => new WireMockAspNetCoreLogger(_logger, categoryName);
public void Dispose() { }
}
+3
View File
@@ -62,6 +62,9 @@ public class Mapping : IMapping
/// <inheritdoc />
public bool IsProxy => Provider is ProxyAsyncResponseProvider;
/// <inheritdoc />
public bool IsDisabled { get; set; }
/// <inheritdoc />
public bool LogMapping => Provider is not (DynamicResponseProvider or DynamicAsyncResponseProvider);
+6 -2
View File
@@ -27,6 +27,7 @@ public class MappingBuilder : IMappingBuilder
private readonly MappingToFileSaver _mappingToFileSaver;
private readonly IGuidUtils _guidUtils;
private readonly IDateTimeUtils _dateTimeUtils;
private readonly IResponseMessageBuilder _responseMessageBuilder;
/// <summary>
/// Create a MappingBuilder
@@ -43,6 +44,7 @@ public class MappingBuilder : IMappingBuilder
_guidUtils = new GuidUtils();
_dateTimeUtils = new DateTimeUtils();
_responseMessageBuilder = new ResponseMessageBuilder(_dateTimeUtils);
}
internal MappingBuilder(
@@ -51,7 +53,8 @@ public class MappingBuilder : IMappingBuilder
MappingConverter mappingConverter,
MappingToFileSaver mappingToFileSaver,
IGuidUtils guidUtils,
IDateTimeUtils dateTimeUtils
IDateTimeUtils dateTimeUtils,
IResponseMessageBuilder responseMessageBuilder
)
{
_settings = Guard.NotNull(settings);
@@ -60,12 +63,13 @@ public class MappingBuilder : IMappingBuilder
_mappingToFileSaver = Guard.NotNull(mappingToFileSaver);
_guidUtils = Guard.NotNull(guidUtils);
_dateTimeUtils = Guard.NotNull(dateTimeUtils);
_responseMessageBuilder = Guard.NotNull(responseMessageBuilder);
}
/// <inheritdoc />
public IRespondWithAProvider Given(IRequestMatcher requestMatcher, bool saveToFile = false)
{
return new RespondWithAProvider(RegisterMapping, Guard.NotNull(requestMatcher), _settings, _guidUtils, _dateTimeUtils, saveToFile);
return new RespondWithAProvider(RegisterMapping, Guard.NotNull(requestMatcher), _settings, _guidUtils, _dateTimeUtils, _responseMessageBuilder, saveToFile);
}
/// <inheritdoc />
@@ -1,6 +1,5 @@
// Copyright © WireMock.Net
using System.Linq;
using Newtonsoft.Json.Linq;
using WireMock.Util;
@@ -0,0 +1,173 @@
// Copyright © WireMock.Net
using System.Text.Json;
using WireMock.Util;
namespace WireMock.Matchers;
/// <summary>
/// Generic AbstractSystemTextJsonPartialMatcher - uses System.Text.Json instead of Newtonsoft.Json.
/// </summary>
public abstract class AbstractSystemTextJsonPartialMatcher : SystemTextJsonMatcher
{
/// <summary>
/// Initializes a new instance of the <see cref="AbstractSystemTextJsonPartialMatcher"/> class.
/// </summary>
protected AbstractSystemTextJsonPartialMatcher(string value, bool ignoreCase = false, bool regex = false)
: base(value, ignoreCase, regex)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="AbstractSystemTextJsonPartialMatcher"/> class.
/// </summary>
protected AbstractSystemTextJsonPartialMatcher(object value, bool ignoreCase = false, bool regex = false)
: base(value, ignoreCase, regex)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="AbstractSystemTextJsonPartialMatcher"/> class.
/// </summary>
protected AbstractSystemTextJsonPartialMatcher(MatchBehaviour matchBehaviour, object value, bool ignoreCase = false, bool regex = false)
: base(matchBehaviour, value, ignoreCase, regex)
{
}
/// <inheritdoc />
protected override bool IsMatch(JsonElement value, JsonElement? input)
{
if (input == null)
{
return false;
}
var inputElement = input.Value;
// Regex on a string value
if (Regex && value.ValueKind == JsonValueKind.String)
{
var valueAsString = value.GetString()!;
var inputAsString = GetStringValue(inputElement);
var (valid, result) = RegexUtils.MatchRegex(valueAsString, inputAsString);
if (valid)
{
return result;
}
}
// Guid comparison: both strings, both parseable as Guid
if (value.ValueKind == JsonValueKind.String && inputElement.ValueKind == JsonValueKind.String)
{
var valueStr = value.GetString()!;
var inputStr = inputElement.GetString()!;
if (Guid.TryParse(valueStr, out var vg) && Guid.TryParse(inputStr, out var ig))
{
return IsMatch(vg.ToString(), ig.ToString());
}
}
// Type mismatch (after regex/guid checks)
if (value.ValueKind != inputElement.ValueKind)
{
return false;
}
switch (value.ValueKind)
{
case JsonValueKind.Object:
{
var nestedValues = value.EnumerateObject().ToArray();
if (nestedValues.Length == 0)
{
return true;
}
return nestedValues.All(pair =>
{
var selected = SelectElement(inputElement, pair.Name);
return selected != null && IsMatch(pair.Value, selected.Value);
});
}
case JsonValueKind.Array:
{
var valuesArray = value.EnumerateArray().ToArray();
if (valuesArray.Length == 0)
{
return true;
}
var tokenArray = inputElement.EnumerateArray().ToArray();
if (tokenArray.Length == 0)
{
return false;
}
return valuesArray.All(subFilter => tokenArray.Any(subToken => IsMatch(subFilter, subToken)));
}
default:
return IsMatch(GetStringValue(value), GetStringValue(inputElement));
}
}
/// <summary>
/// Check if two strings are a match (matching can be done exact or wildcard).
/// </summary>
protected abstract bool IsMatch(string value, string input);
/// <summary>
/// Selects a <see cref="JsonElement"/> from an object using a key that may be a plain property name,
/// a dotted path (e.g. "a.b.c") or bracket notation (e.g. "['name.with.dot']"),
/// mirroring Newtonsoft's <c>SelectToken</c> + direct indexer fallback.
/// </summary>
private static JsonElement? SelectElement(JsonElement input, string key)
{
if (input.ValueKind != JsonValueKind.Object)
{
return null;
}
// Direct property access (also handles keys containing colons or dots that are literal property names)
if (input.TryGetProperty(key, out var direct))
{
return direct;
}
// Bracket notation: ['property.name.with.dots']
if (key.StartsWith("['") && key.EndsWith("']"))
{
var propertyName = key.Substring(2, key.Length - 4);
return input.TryGetProperty(propertyName, out var bracketValue) ? bracketValue : null;
}
// Dotted path: a.b.c
if (key.Contains('.'))
{
var parts = key.Split('.');
var current = input;
foreach (var part in parts)
{
if (current.ValueKind != JsonValueKind.Object || !current.TryGetProperty(part, out var next))
{
return null;
}
current = next;
}
return current;
}
return null;
}
private static string GetStringValue(JsonElement element)
{
return element.ValueKind == JsonValueKind.String
? element.GetString()!
: element.GetRawText();
}
}
@@ -80,10 +80,10 @@ public class ContentTypeMatcher : WildcardMatcher
/// <inheritdoc />
public override string GetCSharpCodeArguments()
{
return $"new {Name}" +
return $"new {typeof(ContentTypeMatcher).FullName}" +
$"(" +
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
$"{MappingConverterUtils.ToCSharpCodeArguments(_patterns)}, " +
$"new AnyOfTypes.AnyOf<string, WireMock.Models.StringPattern>[] {{ {MappingConverterUtils.ToCSharpCodeArguments(_patterns)} }}, " +
$"{CSharpFormatter.ToCSharpBooleanLiteral(IgnoreCase)}" +
$")";
}
@@ -95,7 +95,7 @@ public class ExactMatcher : IStringMatcher, IIgnoreCaseMatcher
/// <inheritdoc />
public string GetCSharpCodeArguments()
{
return $"new {Name}" +
return $"new {typeof(ExactMatcher).FullName}" +
$"(" +
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
$"{CSharpFormatter.ToCSharpBooleanLiteral(IgnoreCase)}, " +
@@ -1,10 +1,10 @@
// Copyright © WireMock.Net
using System.Linq;
using AnyOfTypes;
using Stef.Validation;
using WireMock.Extensions;
using WireMock.Models;
using WireMock.Types;
using WireMock.Util;
namespace WireMock.Matchers;
@@ -21,7 +21,7 @@ public class FormUrlEncodedMatcher : IStringMatcher, IIgnoreCaseMatcher
/// <inheritdoc />
public MatchBehaviour MatchBehaviour { get; }
private readonly List<(WildcardMatcher Key, WildcardMatcher? Value)> _pairs = [];
private readonly List<(WildcardMatcher Key, WildcardMatcher[]? Values)> KeyValueMatchers = [];
/// <summary>
/// Initializes a new instance of the <see cref="FormUrlEncodedMatcher"/> class.
@@ -91,9 +91,10 @@ public class FormUrlEncodedMatcher : IStringMatcher, IIgnoreCaseMatcher
{
foreach (var nameValue in nameValueCollection)
{
var keyMatcher = new WildcardMatcher(MatchBehaviour.AcceptOnMatch, [nameValue.Key], ignoreCase, MatchOperator);
var valueMatcher = new WildcardMatcher(MatchBehaviour.AcceptOnMatch, [nameValue.Value], ignoreCase, MatchOperator);
_pairs.Add((keyMatcher, valueMatcher));
var keyMatcher = new WildcardMatcher(MatchBehaviour.AcceptOnMatch, nameValue.Key, ignoreCase);
var valueMatchers = nameValue.Value.Select(value => new WildcardMatcher(MatchBehaviour.AcceptOnMatch, value, ignoreCase)).ToArray();
KeyValueMatchers.Add((keyMatcher, valueMatchers));
}
}
}
@@ -119,34 +120,48 @@ public class FormUrlEncodedMatcher : IStringMatcher, IIgnoreCaseMatcher
return MatchResult.From(Name, MatchBehaviourHelper.Convert(MatchBehaviour, score));
}
private bool[] GetMatches(IDictionary<string, string> inputNameValueCollection)
private List<double> GetMatches(IDictionary<string, WireMockList<string>> inputNameValueCollection)
{
var matches = new List<bool>();
if (_pairs.Count > inputNameValueCollection.Count)
var inputPairs = inputNameValueCollection.ToArray();
var rowCount = inputPairs.Length;
var columnCount = KeyValueMatchers.Count;
if (rowCount == 0 && columnCount == 0)
{
matches.AddRange(Enumerable.Repeat(false, _pairs.Count - inputNameValueCollection.Count));
return [];
}
foreach (var inputKeyValuePair in inputNameValueCollection)
var matrix = new double[rowCount][];
for (var row = 0; row < rowCount; row++)
{
var match = false;
foreach (var pair in _pairs)
matrix[row] = new double[columnCount];
var inputKeyValuePair = inputPairs[row];
var inputKey = inputKeyValuePair.Key;
var inputValues = inputKeyValuePair.Value;
for (var column = 0; column < columnCount; column++)
{
var keyMatchResult = pair.Key.IsMatch(inputKeyValuePair.Key).IsPerfect();
if (keyMatchResult)
{
match = pair.Value?.IsMatch(inputKeyValuePair.Value).IsPerfect() ?? false;
if (match)
{
break;
}
}
}
var (keyMatcher, valuesMatchers) = KeyValueMatchers[column];
matches.Add(match);
var keyScore = keyMatcher.IsMatch(inputKey).Score;
var valueScore = valuesMatchers != null ? MatchScores.ToScore(inputValues, valuesMatchers) : MatchScores.Perfect;
matrix[row][column] = Math.Min(keyScore, valueScore);
}
}
return matches.ToArray();
var rowScores = rowCount == 0 ? [] : matrix.Select(row => row.Length == 0 ? MatchScores.Mismatch : row.Max()).ToList();
var columnScores = new List<double>();
for (var column = 0; column < columnCount; column++)
{
columnScores.Add(rowCount == 0 ? MatchScores.Mismatch : matrix.Max(row => row[column]));
}
rowScores.AddRange(columnScores);
return rowScores;
}
/// <inheritdoc />
@@ -167,10 +182,10 @@ public class FormUrlEncodedMatcher : IStringMatcher, IIgnoreCaseMatcher
/// <inheritdoc />
public string GetCSharpCodeArguments()
{
return $"new {Name}" +
return $"new {typeof(FormUrlEncodedMatcher).FullName}" +
$"(" +
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
$"{MappingConverterUtils.ToCSharpCodeArguments(_patterns)}, " +
$"new AnyOfTypes.AnyOf<string, WireMock.Models.StringPattern>[] {{ {MappingConverterUtils.ToCSharpCodeArguments(_patterns)} }}, " +
$"{CSharpFormatter.ToCSharpBooleanLiteral(IgnoreCase)}, " +
$"{MatchOperator.GetFullyQualifiedEnumValue()}" +
$")";
@@ -76,7 +76,7 @@ public class FuncMatcher : IFuncMatcher
public string GetCSharpCodeArguments()
{
var funcType = _stringFunc != null ? "Func<string?, bool>" : "Func<byte[]?, bool>";
return $"new {Name}" +
return $"new {typeof(FuncMatcher).FullName}" +
$"(" +
$"/* {funcType} function */, " +
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}" +
@@ -1,6 +1,5 @@
// Copyright © WireMock.Net
using System.Linq;
using AnyOfTypes;
using Newtonsoft.Json.Linq;
using Stef.Validation;
@@ -13,9 +12,8 @@ namespace WireMock.Matchers;
/// <summary>
/// JsonPathMatcher
/// </summary>
/// <seealso cref="IStringMatcher" />
/// <seealso cref="IObjectMatcher" />
public class JsonPathMatcher : IStringMatcher, IObjectMatcher
/// <seealso cref="IJsonPathMatcher" />
public class JsonPathMatcher : IJsonPathMatcher
{
private readonly AnyOf<string, StringPattern>[] _patterns;
@@ -121,7 +119,7 @@ public class JsonPathMatcher : IStringMatcher, IObjectMatcher
/// <inheritdoc />
public string GetCSharpCodeArguments()
{
return $"new {Name}" +
return $"new {typeof(JsonPathMatcher).FullName}" +
$"(" +
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
$"{MatchOperator.GetFullyQualifiedEnumValue()}, " +
@@ -119,7 +119,7 @@ public class JmesPathMatcher : IStringMatcher, IObjectMatcher
/// <inheritdoc />
public string GetCSharpCodeArguments()
{
return $"new {Name}" +
return $"new {typeof(JmesPathMatcher).FullName}" +
$"(" +
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
$"{MatchOperator.GetFullyQualifiedEnumValue()}, " +
@@ -1,11 +1,12 @@
// Copyright © WireMock.Net
using System.Linq;
using System.Collections;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Stef.Validation;
using WireMock.Extensions;
using WireMock.Serialization;
using WireMock.Util;
using JsonUtils = WireMock.Util.JsonUtils;
namespace WireMock.Matchers;
@@ -31,6 +32,11 @@ public class JsonMatcher : IJsonMatcher
/// </summary>
public bool Regex { get; }
/// <summary>
/// Ignore array order when comparing
/// </summary>
public bool IgnoreArrayOrder { get; }
private readonly JToken _valueAsJToken;
/// <summary>
@@ -39,7 +45,8 @@ public class JsonMatcher : IJsonMatcher
/// <param name="value">The string value to check for equality.</param>
/// <param name="ignoreCase">Ignore the case from the PropertyName and PropertyValue (string only).</param>
/// <param name="regex">Support Regex.</param>
public JsonMatcher(string value, bool ignoreCase = false, bool regex = false) : this(MatchBehaviour.AcceptOnMatch, value, ignoreCase, regex)
/// <param name="ignoreArrayOrder">Ignore array element order when comparing.</param>
public JsonMatcher(string value, bool ignoreCase = false, bool regex = false, bool ignoreArrayOrder = false) : this(MatchBehaviour.AcceptOnMatch, value, ignoreCase, regex, ignoreArrayOrder)
{
}
@@ -49,7 +56,8 @@ public class JsonMatcher : IJsonMatcher
/// <param name="value">The object value to check for equality.</param>
/// <param name="ignoreCase">Ignore the case from the PropertyName and PropertyValue (string only).</param>
/// <param name="regex">Support Regex.</param>
public JsonMatcher(object value, bool ignoreCase = false, bool regex = false) : this(MatchBehaviour.AcceptOnMatch, value, ignoreCase, regex)
/// <param name="ignoreArrayOrder">Ignore array element order when comparing.</param>
public JsonMatcher(object value, bool ignoreCase = false, bool regex = false, bool ignoreArrayOrder = false) : this(MatchBehaviour.AcceptOnMatch, value, ignoreCase, regex, ignoreArrayOrder)
{
}
@@ -60,16 +68,18 @@ public class JsonMatcher : IJsonMatcher
/// <param name="value">The value to check for equality.</param>
/// <param name="ignoreCase">Ignore the case from the PropertyName and PropertyValue (string only).</param>
/// <param name="regex">Support Regex.</param>
public JsonMatcher(MatchBehaviour matchBehaviour, object value, bool ignoreCase = false, bool regex = false)
/// <param name="ignoreArrayOrder">Ignore array element order when comparing.</param>
public JsonMatcher(MatchBehaviour matchBehaviour, object value, bool ignoreCase = false, bool regex = false, bool ignoreArrayOrder = false)
{
Guard.NotNull(value);
MatchBehaviour = matchBehaviour;
IgnoreCase = ignoreCase;
Regex = regex;
IgnoreArrayOrder = ignoreArrayOrder;
Value = value;
_valueAsJToken = JsonUtils.ConvertValueToJToken(value);
_valueAsJToken = ConvertValueToJToken(value);
}
/// <inheritdoc />
@@ -83,7 +93,7 @@ public class JsonMatcher : IJsonMatcher
{
try
{
var inputAsJToken = JsonUtils.ConvertValueToJToken(input);
var inputAsJToken = ConvertValueToJToken(input);
var match = IsMatch(RenameJToken(_valueAsJToken), RenameJToken(inputAsJToken));
score = MatchScores.ToScore(match);
@@ -100,12 +110,13 @@ public class JsonMatcher : IJsonMatcher
/// <inheritdoc />
public virtual string GetCSharpCodeArguments()
{
return $"new {Name}" +
return $"new {typeof(JsonMatcher).FullName}" +
$"(" +
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
$"{CSharpFormatter.ConvertToAnonymousObjectDefinition(Value, 3)}, " +
$"{CSharpFormatter.ConvertToAnonymousObjectDefinition(Value, 3)}, " +
$"{CSharpFormatter.ToCSharpBooleanLiteral(IgnoreCase)}, " +
$"{CSharpFormatter.ToCSharpBooleanLiteral(Regex)}" +
$"{CSharpFormatter.ToCSharpBooleanLiteral(Regex)}, " +
$"{CSharpFormatter.ToCSharpBooleanLiteral(IgnoreArrayOrder)}" +
$")";
}
@@ -182,6 +193,13 @@ public class JsonMatcher : IJsonMatcher
return false;
}
if (IgnoreArrayOrder)
{
// Sort both arrays by their string representation and compare
valueArray = valueArray.OrderBy(t => t.ToString()).ToArray();
inputArray = inputArray.OrderBy(t => t.ToString()).ToArray();
}
return !valueArray.Where((valueToken, index) => !IsMatch(valueToken, inputArray[index])).Any();
default:
@@ -241,6 +259,18 @@ public class JsonMatcher : IJsonMatcher
return new JObject(renamedProperties);
}
private static JToken ConvertValueToJToken(object value)
{
// Check if JToken, string, IEnumerable or object
return value switch
{
JToken tokenValue => tokenValue,
string stringValue => JsonConvert.DeserializeObject<JToken>(stringValue, JsonSerializationConstants.JsonDeserializerSettingsWithDateParsingNone)!,
IEnumerable enumerableValue => JArray.FromObject(enumerableValue),
_ => JObject.FromObject(value),
};
}
private static string? ToUpper(string? input)
{
return input?.ToUpperInvariant();
@@ -41,7 +41,7 @@ public class JsonPartialMatcher : AbstractJsonPartialMatcher
/// <inheritdoc />
public override string GetCSharpCodeArguments()
{
return $"new {Name}" +
return $"new {typeof(JsonPartialMatcher).FullName}" +
$"(" +
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
$"{CSharpFormatter.ConvertToAnonymousObjectDefinition(Value, 3)}, " +
@@ -41,7 +41,7 @@ public class JsonPartialWildcardMatcher : AbstractJsonPartialMatcher
/// <inheritdoc />
public override string GetCSharpCodeArguments()
{
return $"new {Name}" +
return $"new {typeof(JsonPartialWildcardMatcher).FullName}" +
$"(" +
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
$"{CSharpFormatter.ConvertToAnonymousObjectDefinition(Value, 3)}, " +
@@ -141,7 +141,7 @@ public class LinqMatcher : IObjectMatcher, IStringMatcher
/// <inheritdoc />
public string GetCSharpCodeArguments()
{
return $"new {Name}" +
return $"new {typeof(LinqMatcher).FullName}" +
$"(" +
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
$"{MatchOperator.GetFullyQualifiedEnumValue()}, " +
@@ -1,7 +1,5 @@
// Copyright © WireMock.Net
using System.Linq;
namespace WireMock.Matchers.Request;
/// <summary>
@@ -30,7 +28,7 @@ public class RequestMatchResult : IRequestMatchResult
return AddMatchDetail(new MatchDetail
{
Name = matcherType.Name.Replace("RequestMessage", string.Empty),
MatcherType = matcherType,
MatcherType = matcherType.Name,
Score = score,
Exception = exception
});
@@ -1,8 +1,8 @@
// Copyright © WireMock.Net
using System.Linq;
using Stef.Validation;
using WireMock.Matchers.Helpers;
using WireMock.Types;
using WireMock.Util;
namespace WireMock.Matchers.Request;
@@ -35,7 +35,7 @@ public class RequestMessageBodyMatcher : IRequestMatcher
/// <summary>
/// The body data function for FormUrlEncoded
/// </summary>
public Func<IDictionary<string, string>?, bool>? MatchOnBodyAsFormUrlEncodedFunc { get; }
public Func<IDictionary<string, WireMockList<string>>?, bool>? MatchOnBodyAsFormUrlEncodedFunc { get; }
/// <summary>
/// The matchers.
@@ -117,7 +117,7 @@ public class RequestMessageBodyMatcher : IRequestMatcher
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
/// </summary>
/// <param name="func">The function.</param>
public RequestMessageBodyMatcher(Func<IDictionary<string, string>?, bool> func)
public RequestMessageBodyMatcher(Func<IDictionary<string, WireMockList<string>>?, bool> func)
{
MatchOnBodyAsFormUrlEncodedFunc = Guard.NotNull(func);
}
@@ -142,6 +142,9 @@ public class RequestMessageBodyMatcher : IRequestMatcher
MatchOperator = matchOperator;
}
/// <inheritdoc />
public RequestMatcherType Type => RequestMatcherType.Body;
/// <inheritdoc />
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
{
@@ -31,6 +31,9 @@ public class RequestMessageBodyMatcher<T> : IRequestMatcher
Func = Guard.NotNull(func);
}
/// <inheritdoc />
public RequestMatcherType Type => RequestMatcherType.BodyOfT;
/// <inheritdoc />
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
{
@@ -74,6 +74,9 @@ public class RequestMessageClientIPMatcher : IRequestMatcher
Funcs = Guard.NotNull(funcs);
}
/// <inheritdoc />
public RequestMatcherType Type => RequestMatcherType.ClientIP;
/// <inheritdoc />
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
{
@@ -20,6 +20,11 @@ public abstract class RequestMessageCompositeMatcher : IRequestMatcher
/// </value>
private IEnumerable<IRequestMatcher> RequestMatchers { get; }
/// <summary>
/// Selected type to choose the matcher from <see cref="RequestMatchers"/> which will immediately return a mismatch.
/// </summary>
internal RequestMatcherType? EarlyMatcherType { get; private protected set; }
/// <summary>
/// Initializes a new instance of the <see cref="RequestMessageCompositeMatcher"/> class.
/// </summary>
@@ -31,6 +36,9 @@ public abstract class RequestMessageCompositeMatcher : IRequestMatcher
_type = type;
}
/// <inheritdoc />
public RequestMatcherType Type => RequestMatcherType.Composite;
/// <inheritdoc />
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
{
@@ -39,6 +47,13 @@ public abstract class RequestMessageCompositeMatcher : IRequestMatcher
return MatchScores.Mismatch;
}
var earlyMatcher = new RequestMessageEarlyMatcher(EarlyMatcherType, RequestMatchers);
var earlyMatchResult = earlyMatcher.GetMatchingScore(requestMessage, requestMatchResult);
if (!MatchScores.IsPerfect(earlyMatchResult))
{
return MatchScores.Mismatch;
}
if (_type == CompositeMatcherType.And)
{
return RequestMatchers.Average(requestMatcher => requestMatcher.GetMatchingScore(requestMessage, requestMatchResult));
@@ -1,7 +1,6 @@
// Copyright © WireMock.Net
using Stef.Validation;
using System.Linq;
namespace WireMock.Matchers.Request;
@@ -93,6 +92,9 @@ public class RequestMessageCookieMatcher : IRequestMatcher
Name = string.Empty; // Not used when Func, but set to a non-null valid value.
}
/// <inheritdoc />
public RequestMatcherType Type => RequestMatcherType.Cookie;
/// <inheritdoc />
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
{
@@ -0,0 +1,35 @@
// Copyright © WireMock.Net
namespace WireMock.Matchers.Request;
/// <summary>
/// Return the mismatch if the matching score of matchers is not perfect.
/// </summary>
internal sealed class RequestMessageEarlyMatcher(
RequestMatcherType? earlyMatcherType,
IEnumerable<IRequestMatcher> requestMatchers) : IRequestMatcher
{
/// <inheritdoc />
public RequestMatcherType Type => RequestMatcherType.Composite;
/// <inheritdoc />
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
{
if (earlyMatcherType is null)
{
return MatchScores.Perfect;
}
var earlyMatchers = requestMatchers
.Where(m => m.Type == earlyMatcherType)
.ToList();
if (earlyMatchers.Count is 0)
{
return MatchScores.Perfect;
}
var compositeMatcher = new RequestBuilders.Request(earlyMatchers);
return compositeMatcher.GetMatchingScore(requestMessage, requestMatchResult);
}
}
@@ -1,6 +1,5 @@
// Copyright © WireMock.Net
using System.Linq;
using Stef.Validation;
using WireMock.Types;
@@ -12,7 +11,7 @@ namespace WireMock.Matchers.Request;
/// <inheritdoc cref="IRequestMatcher"/>
public class RequestMessageHeaderMatcher : IRequestMatcher
{
private const string _name = nameof(RequestMessageCookieMatcher);
private const string _name = nameof(RequestMessageHeaderMatcher);
/// <summary>
/// MatchBehaviour
@@ -106,6 +105,9 @@ public class RequestMessageHeaderMatcher : IRequestMatcher
Name = string.Empty; // Not used when Func, but set to a non-null valid value.
}
/// <inheritdoc />
public RequestMatcherType Type => RequestMatcherType.Header;
/// <inheritdoc />
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
{
Loaded 100 of 311 files, more files were not shown because too many files have changed in this diff. Show more