From 744bbb445c7d61be1b8d045edb9a58f6d6299d4e Mon Sep 17 00:00:00 2001 From: giles Date: Sun, 7 Jun 2026 13:40:02 +0000 Subject: [PATCH] =?UTF-8?q?commerce:=20end-to-end=20composition=20integrat?= =?UTF-8?q?ion=20suite=20(19=20tests)=20=E2=80=94=20hardening?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tests/integration.sx — one narrative across every module: catalog -> stock check -> quote (promo+stack+tax) -> attribution -> order flow -> payment envelope -> settle -> recon -> refund flow -> ledger mismatch, asserting the seams tie together with consistent numbers. Proves the three-substrate composition (minikanren pricing + flow lifecycle + persist ledger) end to end. Total 297/297 across 18 suites. Co-Authored-By: Claude Opus 4.8 (1M context) --- lib/commerce/conformance.sh | 2 +- lib/commerce/scoreboard.json | 7 +- lib/commerce/scoreboard.md | 3 +- lib/commerce/tests/integration.sx | 104 ++++++++++++++++++++++++++++++ plans/commerce-on-sx.md | 9 ++- 5 files changed, 119 insertions(+), 6 deletions(-) create mode 100644 lib/commerce/tests/integration.sx diff --git a/lib/commerce/conformance.sh b/lib/commerce/conformance.sh index a88fc390..2c4168bc 100755 --- a/lib/commerce/conformance.sh +++ b/lib/commerce/conformance.sh @@ -17,7 +17,7 @@ if [ ! -x "$SX_SERVER" ]; then exit 1 fi -SUITES=(catalog cart price api promo stack quote ledger order recon federation attribution payment window nettax stock refund) +SUITES=(catalog cart price api promo stack quote ledger order recon federation attribution payment window nettax stock refund integration) OUT_JSON="lib/commerce/scoreboard.json" OUT_MD="lib/commerce/scoreboard.md" diff --git a/lib/commerce/scoreboard.json b/lib/commerce/scoreboard.json index e5ba2339..1c4e22b2 100644 --- a/lib/commerce/scoreboard.json +++ b/lib/commerce/scoreboard.json @@ -16,9 +16,10 @@ "window": {"pass": 19, "fail": 0}, "nettax": {"pass": 11, "fail": 0}, "stock": {"pass": 19, "fail": 0}, - "refund": {"pass": 20, "fail": 0} + "refund": {"pass": 20, "fail": 0}, + "integration": {"pass": 19, "fail": 0} }, - "total_pass": 278, + "total_pass": 297, "total_fail": 0, - "total": 278 + "total": 297 } diff --git a/lib/commerce/scoreboard.md b/lib/commerce/scoreboard.md index 4e90d445..31d558e9 100644 --- a/lib/commerce/scoreboard.md +++ b/lib/commerce/scoreboard.md @@ -21,4 +21,5 @@ _Generated by `lib/commerce/conformance.sh`_ | nettax | 11 | 0 | 11 | | stock | 19 | 0 | 19 | | refund | 20 | 0 | 20 | -| **Total** | **278** | **0** | **278** | +| integration | 19 | 0 | 19 | +| **Total** | **297** | **0** | **297** | diff --git a/lib/commerce/tests/integration.sx b/lib/commerce/tests/integration.sx new file mode 100644 index 00000000..9803f81f --- /dev/null +++ b/lib/commerce/tests/integration.sx @@ -0,0 +1,104 @@ +;; lib/commerce/tests/integration.sx — end-to-end composition proof. +;; Uses (commerce-test name got expected) provided by conformance.sh. +;; +;; One narrative across every module: catalog → stock check → quote +;; (promo+stack+tax) → order flow → payment envelope → settle → recon → refund. +;; Proves the seams tie together with consistent numbers (the project's thesis: +;; minikanren pricing + flow lifecycle + persist ledger compose). +;; Builds one flow env with BOTH the order and refund flows. + +(define env (order-make-env)) +(define _rf (refund-flow-load! env)) +(define b (persist/mem-backend)) + +(define + cat + (make-catalog + (list + (list "widget" 1000 :standard) + (list "book" 800 :zero-rated)) + (list (list "widget" :small -200)) + (list (list "widget" :small 10) (list "book" :none 5)))) + +(define + rules + (list + (list :uk :standard :guest 2000) + (list :uk :zero-rated :guest 0))) + +(define ctx (make-pricing-context cat rules :uk :guest)) + +(define + ruleset + (list + (list :percent "TEN" :standard 1000) + (list :fixed "FIVE" 0 50))) + +;; widget :small x2 → unit 800, extended 1600 (standard); book x1 → 800 (zero-rated) +(define + cart + (list (list "widget" :small 2) (list "book" :none 1))) + +;; 1. stock gating passes (widget:small 10 >= 2) +(commerce-test "int-can-reserve" (can-reserve? cat cart) true) + +;; 2. quote ties the whole pricing pipeline together +;; subtotal 2400; discount TEN 160 + FIVE 50 = 210; tax 1600@20% = 320; +;; total 2400 - 210 + 320 = 2510 +(define q (cart-quote ctx cart ruleset (list))) +(commerce-test "int-quote-subtotal" (quote-subtotal q) 2400) +(commerce-test "int-quote-discount" (quote-discount q) 210) +(commerce-test "int-quote-tax" (quote-tax q) 320) +(commerce-test "int-quote-total" (quote-total q) 2510) + +;; 3. attribution explains where the discount landed +(commerce-test + "int-attribution" + (codes-for-line ctx cart ruleset (list "widget" :small 2)) + (list "TEN")) +(commerce-test + "int-order-level" + (order-level-codes ctx cart ruleset) + (list "FIVE")) + +;; 4. order carries the quote total into the ledger; suspends at payment +(define oid "INT-1") +(define id (order-begin! env b oid 1000 q)) +(commerce-test "int-order-total-from-quote" (order-total b oid) 2510) +(commerce-test "int-waiting-payment" (order-flow-waiting env id) "payment") + +;; 5. the payment envelope reflects the quoted total +(commerce-test + "int-payment-envelope" + (payment-request b oid :GBP "https://shop/return") + {:order "INT-1" :amount 2510 :return-url "https://shop/return" :currency :GBP}) + +;; 6. settle the quoted amount → reconciles exactly +(commerce-test + "int-settled" + (order-settle! env b id oid "pay-int" 1002 2510) + :settled) +(commerce-test "int-status-fulfilled" (order-status b oid) :fulfilled) +(commerce-test "int-recon-ok" (order-recon b oid) :ok) + +;; 7. partial refund via its own flow → recon moves to underpaid +(define rid (refund-begin! env b oid "rf-int" 2000 510)) +(commerce-test "int-refund-approve" (refund-approve! env rid) :approved) +(commerce-test + "int-refund-settle" + (refund-settle! env b rid oid "rf-int" 2001 510) + :settled) +(commerce-test + "int-refunded-amount" + (order-refunded-amount-of (order-events b oid)) + 510) +(commerce-test "int-recon-after-refund" (order-recon b oid) :underpaid) + +;; 8. ledger reconciliation flags the now-mismatched order +(commerce-test + "int-mismatch" + (mismatched-orders b) + (list (order-stream "INT-1"))) + +;; 9. distinct flow ids for the order and the refund +(commerce-test "int-distinct-flow-ids" (not (= id rid)) true) diff --git a/plans/commerce-on-sx.md b/plans/commerce-on-sx.md index d6b6e1cb..833070e7 100644 --- a/plans/commerce-on-sx.md +++ b/plans/commerce-on-sx.md @@ -21,7 +21,7 @@ reconciliation — all auditable via the event log. ## Status (rolling) -`bash lib/commerce/conformance.sh` → **278/278** (17 suites; + refund) — **roadmap + full Phase 5 backlog complete** +`bash lib/commerce/conformance.sh` → **297/297** (18 suites; + integration) — **roadmap + Phase 5 backlog + e2e composition proof complete** ## Ground rules @@ -101,6 +101,13 @@ that unlocks the most tests per effort each iteration. agnostic; `order-settle!(ref, amount)` is the resume seam. ## Progress log +- 2026-06-07 — `tests/integration.sx` (hardening): end-to-end composition proof — + one narrative across every module (catalog → stock check → quote[promo+stack+tax] + → attribution → order flow → payment envelope → settle → recon → refund flow → + ledger mismatch) asserting the seams tie together with consistent numbers + (subtotal 2400, discount 210, tax 320, total 2510; settle→:ok; refund 510→ + :underpaid; mismatch flagged). Proves the three-substrate composition. One env + with both order+refund flows. integration suite 19/19; total 297/297 (18 suites). - 2026-06-07 — `refund.sx` (**Phase 5 backlog complete**): refund lifecycle as a second flow-on-sx flow `(lambda (oid) (begin (request 'approve oid) (request 'settle oid)))` — two suspension points (approval = human/policy decision,