Repository navigation
Slice 005: pay with Tendr - #19
Merged
Merged
Conversation
…th twins Tendr (src/tendr/Tendr, port 42040) is a fake card vendor on Wolverine.Http and Marten's event store: idempotent POST /v1/authorizations keyed by the order id, a four-entry test-card table, GET by id. Not a twin, not counted. POST /orders takes an optional card. No card behaves exactly as before. A declined card is a 402 after the three existing guards; a vendor that refuses the connection or runs past the two-second timeout is a 503. Before twin: IPaymentGateway port and PaymentAuthorization in Application, TendrPaymentGateway typed client with options and wire DTOs in Infrastructure, two exceptions mapped in the filter, migration AddOrderPayment. 11 files created, 8 edited. After twin: Payments/Tendr.cs, plus an AuthorizeAsync rung and a Validate(Payment?) guard in PlaceOrder.cs. Decide and the three guards are unchanged. 1 file created, 3 edited. The column default on PaymentStatus keeps Wolverine's startup migration from dropping a populated orders table. Suite: 70/70 contract (35 x 2, the 27 existing scenarios unchanged), 19/19 after-twin facts, 19/19 Tendr. A Tendr host per twin collection runs on Kestrel against that collection's PostgreSQL; Wolverine hosts in the test process are built one at a time so each keeps its own application assembly. OpenSpec change archived; orders spec synced. Inventory, build log, README, CLAUDE.md, frontend spec and demo page updated.
…keAsync The optional step from the slice 005 hand-off, kept to Tendr; neither twin changes. Tendr maps MapWolverineHttpTransportEndpoints() beside its REST endpoints, and a thin AuthorizeCardHandler answers AuthorizeCard sent as a message, using the envelope id as the idempotency key. The request rules and the stream start moved into AuthorizeCardEndpoint.Invalid and Start so the REST call and the message share them. tests/Tendr.Tests/HttpTransportFacts.cs: a plain Wolverine sender host routes AuthorizeCard to Tendr's /_wolverine/invoke and gets an approval back from InvokeAsync<CardAuthorization>; Tendr's store holds the authorization after. On the pinned WolverineFx.Http 6.30.0 the HTTP transport resolves only https:// endpoints (http:// is first handled in 6.34.0), so the test serves Tendr over HTTPS with a certificate it generates itself. No package bump. Root dotnet test: 70/70, 19/19, Tendr.Tests 20/20. Also corrects the slice spec: OrderConfiguration is unchanged, and the after twin's PaymentStatus column carries a database default.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Slice 005, the first v1.1 change after KCDC: both twins call a third-party card vendor over HTTP on PlaceOrder's load leg, and the shared suite runs every payment scenario against a real vendor host on a real socket. Spec:
docs/slices/005-pay-with-tendr.md. Decisions and evidence: the two 2026-09-14 entries indocs/build-log.md.What changed
src/tendr/Tendr, port 42040): a fake card vendor on Wolverine.Http and Marten's event store, schematendr. IdempotentPOST /v1/authorizations,GET /v1/authorizations/{id}, a four-entry test-card table. Not a twin, not in the scorecard.POST /orderstakes an optionalcard. No card behaves exactly as before, and the body has nopaymentmember. A declined card is a402after the three existing guards; a vendor that refuses the connection or runs past the two-second timeout is a503.GET /orders/{id}mirrors the payment.IPaymentGatewayport andPaymentAuthorizationoutcome in Application,TendrPaymentGatewaytyped client with options and wire DTOs in Infrastructure, two exceptions mapped in the filter,PaymentStatusenum,PaymentDto, migrationAddOrderPayment. The handler now injects eight dependencies.Payments/Tendr.cs; inPlaceOrder.csa[WolverineBefore] AuthorizeAsyncrung and aValidate(Payment?)guard;Decide,LoadAsyncand the three existing guards are unchanged. Generated code confirms the chain runsLoadAsync,Validate,AuthorizeAsync,Validate,Post.PaymentScenarios(6) andPaymentOutageScenarios(2), both twins.OrderScenarios.csand every other existing scenario file have no diff, so the Marten and template experiments are untouched.AuthorizeCardover Wolverine's HTTP transport, andtests/Tendr.TestsprovesInvokeAsync<CardAuthorization>end to end on the pinned 6.30.0.ordersspec synced. README, CLAUDE.md, inventory, frontend page and spec updated.Results
dotnet test: contract 70/70 (35 x 2), after-twin facts 19/19, Tendr 20/20, 0 warnings. About 16 s warm and 28 s cold, against 11 to 12 s and 20 s formainmeasured on the same machine.Worth a look
Postkeeps an unusedPlaceOrder commandparameter, because Wolverine.Http reads the body type from the endpoint method's own parameters.AlwaysUseServiceLocationFor<TendrClient>(), because a typedHttpClientis an opaque factory to Wolverine 6's codegen.after.ordersin the compose database (one test order lost) because the new NOT NULL column had no default. Fixed with a column default, verified against a throwaway database.tests/Tendr.Testsin the slnx adds one PostgreSQL container, at no measurable wall-clock cost. Removing it from the slnx is one line if the live run should stay at four containers.