98 lines
6.2 KiB
Markdown
98 lines
6.2 KiB
Markdown
# `pkg/cart` — mutation inventory
|
|
|
|
Every mutation file under `pkg/cart/mutation_*.go` plus the slice of cart
|
|
state it touches. The grouping is **subjective** ("Subscription lifecycle",
|
|
"Discount surface", etc.) — used as a starting point for collapsing the
|
|
file-per-mutation pattern flagged in `agents.md`. Move two related
|
|
mutations into one file first; if the test surface stays smaller than the
|
|
two-file version, the grouping is real.
|
|
|
|
| File | Concern | State it READS | State it WRITES |
|
|
| --------------------------------------------------- | -------------------- | ----------------------------- | ---------------------------------------- |
|
|
| `mutation_items.go` (`mutation_add_item.go` and `mutation_remove_item.go` merged; tests stay in `mutation_add_item_test.go` + `mutation_remove_item_test.go`) | Line item add/remove | `state.Items` | `state.Items`, `state.UpdatedAt` |
|
|
| `mutation_change_quantity.go` | Line item quantity | `state.Items[idx]` | `state.Items[idx]`, `state.UpdatedAt` |
|
|
| `mutation_clear_cart.go` | Wholesale clear | — | `state.Items`, `state.Vouchers`, `state.UpdatedAt` |
|
|
| `mutation_set_user_id.go` | Ownership | — | `state.UserID`, `state.UpdatedAt` |
|
|
| `mutation_set_cart_type.go` (+ test) | Cart type | — | `state.Type`, `state.UpdatedAt` |
|
|
| `mutation_add_voucher.go` | Discount surface | `state.Vouchers` | `state.Vouchers`, `state.LineTotals` (re-eval), `state.UpdatedAt` |
|
|
| `mutation_set_custom_fields.go` (+ tests) | Custom K/V | `state.CustomFields` | `state.CustomFields`, `state.UpdatedAt` |
|
|
| `mutation_set_recovery_contact.go` (+ tests) | Recovery | — | `state.Recovery`, `state.UpdatedAt` |
|
|
| `mutation_subscription_added.go` | Subscription | `state.Items` | `state.Subscriptions`, `state.UpdatedAt` |
|
|
| `mutation_upsert_subscriptiondetails.go` | Subscription details | `state.Subscriptions` | `state.Subscriptions`, `state.UpdatedAt` |
|
|
| `mutation_markings.go` (`mutation_line_item_marking.go` and `mutation_remove_line_item_marking.go` merged) | Markings apply/remove | `state.Items[idx].Markings` | `state.Items[idx].Markings`, `state.UpdatedAt` |
|
|
|
|
## Delete-test quick check
|
|
|
|
For any proposed grouping, ask: does deleting the merged file concentrate
|
|
complexity, or just move lines? For markings — yes, the shared logic
|
|
(cascade to pricing) makes the merge worthwhile, and the merge is now done
|
|
(see below). For `voucher` vs `custom_fields` — no, those read different
|
|
state and have different error contracts; keep separate.
|
|
|
|
### What is already merged
|
|
|
|
`pkg/cart/mutation_items.go` (added 2025-07) — AddItem and RemoveItem
|
|
both touch `state.Items` and share the `ErrPaymentInProgress`,
|
|
`decodeExtra`, and `getOrgPrice` helpers. Merge passed the delete-test
|
|
checks: the receiver `*CartMutationContext`, same grain (`*CartGrain`),
|
|
same mutation-registry dispatch. Both `mutation_*_test.go` files remain
|
|
because they assert specific method behaviour, not generic package
|
|
behaviour. Verified: `go test -count=1 ./pkg/cart/...` clean (voucher test
|
|
which references `ErrPaymentInProgress` still green).
|
|
|
|
#### Table corrections when merging
|
|
|
|
When the two source rows collapsed into one, two corrections landed in
|
|
the merge:
|
|
|
|
- AddItem's old READS column listed `state.Vouchers (for stacking)`.
|
|
That clause was **incorrect** — voucher stacking is `AddVoucher`'s
|
|
concern, not AddItem's. The merge drops the clause because reading
|
|
`mutation_add_item.go` confirms AddItem only reads `state.Items`.
|
|
This is a documentation correction, not a behaviour change.
|
|
- Both `State it READS / WRITES` columns originally had `(both)`
|
|
annotations to remind that two distinct mutations were sharing the
|
|
row. After collapse the row already says `Line item add/remove`, so
|
|
the `(both)` markers are noise — dropped.
|
|
|
|
If a future merge lands, follow the same pattern: read the actual source
|
|
to verify the row's claimed state surface, and prune label-noise before
|
|
compressing two rows into one.
|
|
|
|
`pkg/cart/mutation_markings.go` (added 2025-07) — LineItemMarking and
|
|
RemoveLineItemMarking both touch `state.Items[idx].Markings` and share
|
|
the same item-lookup loop (find by ID, mutate `Marking` field). Merge
|
|
passed the delete-test: the shared write surface and identical error
|
|
contract ("item with ID %d not found") make the grouping real. No test
|
|
files existed for either mutation, so the test surface is unchanged.
|
|
Verified: `go build ./pkg/cart/...` and `go test -count=1 ./pkg/cart/...`
|
|
clean.
|
|
|
|
### NOT to merge next
|
|
|
|
`mutation_change_quantity.go` looks like an obvious candidate for the next
|
|
merge (same receiver, same grain, same `state.Items` write surface). It
|
|
is **not** — quantity has a partial-line-drop arithmetic contract that
|
|
add/remove do not:
|
|
|
|
- `AddItem` always merges or appends; never sets quantity down.
|
|
- `RemoveItem` always removes whole lines; never decrements quantity.
|
|
- `ChangeQuantity` may drop a line entirely if quantity reaches 0.
|
|
|
|
If you merge `ChangeQuantity` first, doing the same delete-test check
|
|
yields: deleting the merged file leaves the partial-line arithmetic
|
|
logic stranded in `Apply`, which IS a real concentration (good). But
|
|
the inverse: if `AddItem` later grows a "decrement" edge case, the
|
|
merged file will balloon and a re-split becomes expensive. Either
|
|
keep `ChangeQuantity` separate, OR merge all three with the merge
|
|
scoped to a "line item lifecycle" package and the partial-line
|
|
arithmetic under a clearly-named helper.
|
|
|
|
## How this maps onto a refactor
|
|
|
|
1. Pick a row with shared logic (markings, subscription details).
|
|
2. Move related mutations + their tests into one file under
|
|
`pkg/cart/<concern>.go`.
|
|
3. Re-run `go test ./pkg/cart/...` — same assertions, smaller surface.
|
|
4. Update this table to reflect the new file layout.
|