Skip to content

test(compat): measure plugin DSL coverage with the stacked plugin PRs (do not merge) - #42

Draft
YOU54F wants to merge 39 commits into
feat/compat-suite-pluginsfrom
compat/plugins-integration
Draft

YOU54F wants to merge 39 commits into
feat/compat-suite-pluginsfrom
compat/plugins-integration

Conversation

@YOU54F

@YOU54F YOU54F commented Oct 29, 2026

Copy link
Copy Markdown
Owner

Summary

Stacks the PactNet plugin PRs on top of the plugin runner (passes 2–5). Plugin scenarios move from the FFI onto the DSL wherever the new interfaces allow it.

Stacked on the pass-1 PR #41 (feat/compat-suite-plugins). Merges in:

The grpc wrapper lives in pact-net-extensions main. The avro wrapper is in pact-net-extensions PRs #1 (avro-http) and #2 (avro-async).

This branch is for compat measurement only. Don't merge it. It contains the four PRs above.

Depends on the suite branch feat/plugin-features (YOU54F/pact-compatibility-suite#1). The plugin features are included only when PACT_COMPAT_SUITE points at a checkout that has features/V4/plugins. Needs plugins matt 0.1.1, csv 0.0.6, avro 0.1.3 and protobuf 0.8.0 installed.

PACT_COMPAT_SUITE=/path/to/pact-compatibility-suite dotnet test tests/PactNet.Compatibility.Tests

Each run writes the routes it took to tests/PactNet.Compatibility.Tests/bin/Debug/net10.0/plugin-routes.txt.

Results

48/48 pass on every pass. V1 consumer is still 24 pass. There are no src/ changes beyond resolving merge conflicts.

Counts are DSL/FFI. A scenario counts as DSL only if it makes no FFI calls.

pass commit pact_ffi http 14 message 10 sync 6 transport 13 lifecycle 5 DSL total
1 current f79aca9 0.4.27 0/14 0/10 0/6 0/13 0/5 0
2 +pact-foundation#551 sync plugins b4c6aea 0.5.10 0/14 0/10 0/6 7/6 0/5 7
3 +pact-foundation#562 sync messaging ae0a158 0.5.12 0/14 0/10 0/6 7/6 0/5 7
4 +fork#36 http plugins 51d2bf4 0.5.12 4/10 0/10 0/6 7/6 0/5 11
5 +fork#37 async plugins 03952ce 0.5.12 4/10 6/4 0/6 7/6 0/5 17

Pass 3 had conflicts:

  • FFI version: took 0.5.12.
  • PactExtensions.cs: kept both sides.
  • Test fixtures: regenerated.

Limitations (none raised)

Even with the full stack, 31 of the 48 scenarios still need the FFI:

  • There's no generic WithPlugin(name, version) or arbitrary-transport surface. There are only three per-shape entry points (WithSynchronousPluginInteractions, WithHttpPluginInteractions, WithAsynchronousPluginInteractions), each with one plugin per builder.
  • feat: add consumer-side support for synchronous (request/response) messages pact-foundation/pact-net#562's sync message DSL is JSON-only.
  • The sync and HTTP plugin builders can't write a pact without a passing mock-server test. The sync builder sets the request part only.
  • Writing a sync plugin pact through the HTTP mock server panics in pact_ffi (mock_server.rs:463 unwrap on None).
  • The HTTP plugin builder has no plain body and can't mix plugins.
  • PactVerifier has no plugin-transport API and gives no JSON or mismatch output.
  • There's no DSL for the plugin lifecycle (load errors, cleanup).

🤖 Generated with Claude Code

adamrodger and others added 30 commits September 14, 2026 11:48
This enables creating a nicer API to expose publicly for use by plugin
wrappers.

See: pact-foundation#492
…ilder

Setting the plugin contents completes a synchronous plugin interaction, so
WithContent now returns void instead of the same builder. The plugin splits
one contents document into the request and its responses itself: a second
call would replace the request but append another response, which is why the
builder no longer allows it. Provider states stay chainable via Given.

Adds unit tests for the plugin request and pact builders and a docs page for
the raw plugin API, linked from the README.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Add a public HTTP plugin pact/request/response API on top of the synchronous plugin prerequisite. Load content plugins through the existing factory, create HTTP rather than synchronous-message interactions, and route plugin contents independently to request and response parts.

Reuse HTTP drivers for methods, paths, headers, queries and status; retain mock-server verification, logging, pact writing and plugin cleanup inside PactNet. Keep native handles and drivers internal. Reject repeated contents and response-stage reuse, validate HTTP status, and preserve header/query indexing.

Add focused tests and HTTP plugin guidance. Asynchronous binary plugin message reification remains a separate contribution. Validation: 60 affected builder tests passed, including existing synchronous and HTTP behavior; the external Avro example passed native request/response and provider/mismatch verification with hominux avro 0.1.3 and FFI 0.4.27.
Add transport-free asynchronous plugin pact/message builders and configured consumer verification. Expose generated binary payloads, media type and cloned JSON metadata, write the pact only after successful consumption, and keep native reification and plugin cleanup internal.

Decode the native V4 base64 envelope and free its allocated string. Apply user metadata after plugin content configuration, which can replace native message metadata. Preserve the existing JSON message APIs and add ordering, terminal-content, callback success/failure and disposal tests.

Add an opt-in native Avro 0.1.3 synchronous capability check. With FFI 0.4.27, its unlabelled single-record configuration creates an empty sync request and no responses. Document the need for plugin request/response parts and a separate transport-free sync consumer matching API rather than presenting the transport-based gRPC builder as Avro sync support.

Validation: 109 affected plugin/message/HTTP builder tests passed with the capability check enabled. External Avro async consumer/provider and mismatch checks passed with binary records and metadata. HTTP and gRPC regressions remained green.
…rotobuf plugin 0.8.0

Brings in 92748436 as requested in the PR pact-foundation#551 review.

- pact-ffi 0.4.27 -> 0.5.10; a request header configured with multiple
  values is now matched as one joined value, so the tests and the
  upgrading-to-5 doc reflect that
- pact fixtures regenerated (pactRust metadata, merged X-Request header)
- grpc sample pact regenerated with pact-protobuf-plugin 0.8.0
- grpc sample and plugin docs now request plugin 0.8.0

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Upgrade the Avro async branch native package bootstrap from pact-ffi 0.4.27 to released 0.5.10. The extension's Avro provider verification returned HTTP 200 but plugin content comparison failed with a transport error on Windows under 0.4.27. Repacking the same PactNet source against 0.5.10 makes all five Avro sample tests pass locally, including provider matching and the expected provider mismatch.

All five supported runtime libraries download with published SHA-256 verification. Other legacy PactNet fixture tests may need fixture metadata updates before the general branch suite runs against 0.5.10; this branch change is the Avro async bootstrap prerequisite.
Replace build/install-plugins.sh with the pact-foundation/pact-cli action
and `pact plugin install`, as suggested in review. The plugin is still
pre-installed before the tests run so parallel test projects do not race
each other auto-downloading it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Update native library downloads and refresh Rust version metadata in
integration fixtures and the Orders API sample pact.

Retain existing repeated-header expectations and pact representations.
…ssages

PactNet already supports writing asynchronous (fire-and-forget) message
pacts, but had no way to write V4 synchronous message pacts (used by e.g.
AsyncAPI 3 operations with a reply block), even though the underlying
pact_ffi native library already exposes pactffi_new_sync_message_interaction
and can set request/response bodies and metadata on it via the existing
generic pactffi_with_body/pactffi_with_metadata functions.

This adds:
- IPactV4.WithSynchronousMessageInteractions() entry point
- ISyncMessagePactBuilderV4 / ISyncMessageBuilderV4 / IConfiguredSyncMessageVerifier
  public API, mirroring the existing async IMessagePactBuilderV4 API
- SyncMessageInteractionDriver, wiring the above onto the native FFI calls

The consumer-side Verify<TRequest, TResponse> reads the generated request
contents (matchers stripped, generators applied) via the new
pactffi_sync_message_reify FFI function and feeds it to the handler under
test; the response content is stored but not currently asserted against,
matching the existing async message Verify<T> semantics.

Depends on pactffi_sync_message_reify, proposed in
pact-foundation/pact-reference#555. Until that lands
in a released pact_ffi build, calling ConfiguredSyncMessageVerifier.Verify
will fail with EntryPointNotFoundException - the corresponding FFI
integration test is marked Skip until then.
Removed redundant phrase from summary comment.
Callers of PactNet don't need to know the Pact-internal term "reify" -
GenerateContents describes what the method actually returns. Matches the
pactffi_sync_message_generate_contents rename on the pact-reference side
(pact-foundation/pact-reference#555). The pre-existing async message
Reify()/pactffi_message_reify naming is untouched.
Adds a section to messaging-pacts.md covering WithSynchronousMessageInteractions,
alongside the existing fire-and-forget message docs. Notes that provider
verification isn't covered yet and that this depends on
pactffi_sync_message_generate_contents landing upstream.
Removed information about provider verification support and related issues.
MessageInteractionDriver.Reify() and SyncMessageInteractionDriver.GenerateContents()
copied the FFI-returned string into managed memory but never freed the
native allocation, leaking it on every call.
Verify/VerifyAsync already required the handler to return a TResponse
but never checked it, so a consumer test would pass regardless of what
the handler returned. Compare it against the FFI-generated response
content (matchers stripped, generators applied) via a structural JSON
equality check, so a mismatch fails the test instead of being silently
written to the pact file.
pact-foundation#551 reorganises Drivers/ into Http/, Message/ and Plugins/
subfolders. The sync message driver belongs in Message/ alongside the
async one, since it's the same message-queue-style request/response
category, not a plugin. Namespace stays PactNet.Drivers for now since
the rest of the tree hasn't moved yet; that follows once pact-foundation#551 lands.
pactffi_with_metadata only applies response metadata to responses that
already exist, and the response is created by the terminal
WithResponseJsonContent call, so response metadata was always dropped.
The builder now buffers it and applies it after the response content.

Request/response metadata values are now dynamic and serialised as JSON,
so strings, numbers, booleans and matchers keep their type in the pact
instead of relying on the FFI's parse-or-fallback-to-string behaviour.
fabio-andre-rodrigues and others added 9 commits October 19, 2026 10:40
Un-skip the FFI integration test now that 0.5.10 ships
pactffi_sync_message_generate_contents, and compare the written pact
against a fixture. Add public API tests for
WithSynchronousMessageInteractions covering body matchers, typed
metadata, response metadata matchers and a mismatching response.
Links a sync message interaction to an external resource such as an
AsyncAPI operation via pactffi_add_interaction_reference, written to
comments.references.{group}.{name}. Same signature as the async
WithReference proposed in pact-foundation#559.

The FFI parses the value as JSON, so the driver encodes it to keep a
string reference value a string in the pact.
Verify/VerifyAsync compared the handler's response to the generated
example by exact JSON equality, so a type matcher still required the
example value. They now call pactffi_sync_message_match_response_contents,
which applies the response's matching rules the same way the verifier
does: Match.Type checks the type of the example, Match.Integer and
Match.Decimal the kind of number, Match.Regex the format (e.g. datetimes),
and unexpected keys are allowed. Mismatches are reported by path.

Requires a pact_ffi release with pactffi_sync_message_match_response_contents.
Aligns the sync message integration fixtures with the ffi 0.5.12 /
models 1.3.17 metadata from the pact-ffi upgrade.
…h the DSL

Merge the synchronous plugin interaction DSL (pr551, pact_ffi 0.5.10) and
move the gRPC/matt transport mock server scenarios to
WithSynchronousPluginInteractions. HTTP and message provider verifications
use PactVerifier when they pass, re-running through the FFI verifier for
mismatch details. Pact writes without a mock server, plugin-transport
verification and HTTP/async plugin contents stay on the FFI.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolve conflicts with the plugin DSL: keep both
WithSynchronousMessageInteractions and WithSynchronousPluginInteractions
(using LogToBuffer), drop the duplicated NewSyncMessageInteraction
import, add the PactNet.Drivers.Message usings, take FFI 0.5.12 and
regenerate the integration fixtures against it.

The synchronous message DSL only takes JSON request/response bodies
and cannot load a plugin, so the plugin sync message scenarios stay
on the FFI.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ugh the DSL

HTTP plugin interactions are now recorded lazily and built with
WithHttpPluginInteractions when they share one plugin, have a request line
and no plain body, and run against a mock server (4 plugin_http consumer
scenarios). Pact generation without a mock server, mixed plain/plugin and
multi-plugin pacts stay on the FFI.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ough the DSL

Conflicts: build/download-native-libs.sh keeps pact_ffi 0.5.12 (theirs was
0.5.10); MessageInteractionDriver.Reify takes the try/finally free from
pr-avro-async.

Asynchronous plugin message pacts, consumer and provider side, are now
written with WithAsynchronousPluginInteractions. Successful message
verifications therefore run end to end on the DSL.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants