On this page
What to testWhere the edges are (known code-level gaps to regression-test specifically, not just spot-check)For Quality
What to test
Tenancy (do this first — it's the highest-severity class of bug here)
Every supplier query/mutation scopes by vendorCompanyId; every buyer query/mutation scopes by buyerCompanyId. Verify directly, not just through the UI:
- Vendor A cannot read, edit, delete, or transition Vendor B's listings or orders, even with a guessed/known id (
listingService.js,orderService.jsall usefindFirst({ id, vendorCompanyId })— confirm every mutation path enforces this, not just the list queries). - A buyer cannot receive an order that isn't theirs, or receive using another company's
tradeOrderId. browseListings(buyer marketplace search) should only ever surfaceactive: truelistings — confirm inactive/soft-retired listings never appear.
The seller state machine — test the edges, not just the happy path
Reference: Order Lifecycle & Accounting for the full transition table. Specifically probe:
- Every disallowed transition throws (e.g.
PLACED → DELIVEREDdirectly,DELIVERED → anything,SHIPPED → CANCELLED) — confirm the error message and that no state change/posting occurred. ACCEPTED → SHIPPED(skipping Preparing) andPREPARING → DELIVERED(skipping Shipped) should both succeed — these are valid shortcuts, not bugs, per the coded table.- Confirm
SHIPPED → CANCELLEDis genuinely blocked end-to-end (this is a known, documented gap — see Order Lifecycle) rather than assuming it works because reversal code exists. If product wants this fixed, it needs aTRANSITIONStable change, not just a reversal-logic check. REJECTEDshould only be offered/accepted fromPLACED.
Accounting — balance and idempotency
- Post a fulfilment (mark Shipped or Delivered) and confirm the journal entry balances, using the exact role amounts from Order Lifecycle & Accounting (AR = netRevenue + VAT; COGS pair only present when a cost is resolvable).
- Re-trigger posting for the same order (e.g.
Shipped → Delivered, which re-fires the posting call) and confirm no duplicate journal entry is created — idempotency is supposed to be guaranteed bypostSourceCreated's existing-entry check. - Test COGS resolution specifically: an order line whose product has no matching
InventorySubItemon the supplier's side, or whose matched stock hasamount: 0, should silently contribute0cost — confirm the entry still balances with just the revenue/VAT pair (no COGS/Inventory lines at all) rather than erroring or unbalancing. - Test a GL misconfiguration (e.g. a missing default account mapping) — confirm the order status transition still succeeds (posting failure must not roll back the operational write), and that the posting is retryable rather than silently lost.
Quotas and entitlements
maxListings: create listings up to the plan's limit, confirm the next create is blocked with the exact upgrade message, confirm a per-vendorquotaOverrides.maxListingsoverride takes precedence over the plan's field (both raising and lowering the effective limit).maxImagesPerListing: confirm this one silently truncates the image list rather than blocking the whole listing save — deliberately different behavior from the listing-count quota; a test asserting an error here would be testing for the wrong behavior.RFQ_RESPONDentitlement: test at all three enforcement points independently — (1) thesupplierRfqs/supplierRfqqueries return empty/null (not an error) when not entitled, (2)submitSupplierOfferthrows an explicit upsell error, (3) a downgrade mid-session (entitled at page load, downgraded before submit) still blocks the submit — this proves the mutation-side check isn't relying on stale client state.- Broadcast RFQ visibility: confirm a vendor whose service-area geo matches an
OPENbroadcast RFQ (scope: CITY/REGION/ALL) can see it regardless of theRFQ_BROADCAST_CITY/RFQ_BROADCAST_REGION_ALLentitlement tier — this is current, verified behavior, not a bug, but worth a product sign-off since those entitlement keys otherwise look unused.
The receive bridge — the trickiest data-integrity surface
- Partial mapping: an order with 3 product lines where only 2 are mapped to buyer inventory (
catalogProductIdmatch) must be fully blocked, not partially received — confirm zero inventory/expense side effects occur (no partialInventoryOrder), and the error names all unmatched products. - Double receive: attempt to receive the same order twice — second attempt must fail cleanly with "already been received," and must not create a second
InventoryOrder/Expense. - Status guard: attempt to receive an order still at
PLACED/ACCEPTED/PREPARING— must be blocked, with the current status named in the error. - Service-only orders: an order with only
SERVICElines and no stockable product lines should be blocked ("no stockable product lines to receive") — confirm this doesn't crash rather than error cleanly. - Tax proportioning: confirm the posted expense's tax is the matched-lines' proportional share of the order's total tax, not the full order tax — verify with an order that mixes SERVICE and PRODUCT lines.
- Branch/storage fallback: test a buyer company with no branches at all (should error cleanly, "No branch available"), and a stock line with no
defaultStorageId(should fall back to the company's master storage, or any storage if no master is flagged).
Reorder math
suggestedReorderQty is a pure function — cheap to property-test directly: max(0, preferred − onHand), rounded to 6 decimal places, never negative, handles fractional stock quantities correctly (the module exists specifically to be tested without a database).
Cross-cutting / integration
- Full path: place order (buyer) → accept → ship (seller) → confirm revenue posted (seller books) → receive (buyer) → confirm stock + expense posted (buyer books) → confirm the two sides' journal entries are entirely independent (no shared/reconciling entry, per the "no intercompany mirroring" design).
- Currency/rounding: an order with delivery fee and discount together — confirm subtotal/tax/total rounding matches the accounting entry exactly (this is covered by a unit test for the derivation function, but worth an integration-level check that the resolver-computed order total and the posted journal amounts agree).
Where the edges are (known code-level gaps to regression-test specifically, not just spot-check)
deleteSupplierListingreturnstrueregardless of whether it hard-deleted or soft-retired — if any future UI depends on distinguishing these, it can't from this return value today.SHIPPED → CANCELLEDis unreachable viatransitionSupplierOrdereven though the revenue-reversal code path exists and is unit-tested in isolation — don't let a future refactor "fix" this silently without a product decision, and don't assume integration coverage exists here just because the reversal math is tested.- The resilient-posting retry path considers
RECEIVED/CLOSEDorders still "fulfilled enough to post," while the live transition trigger only fires on enteringSHIPPED/DELIVERED— if a parked posting retries after the buyer has already received the order, confirm it still posts correctly rather than being skipped or erroring.