Money-safety idempotency fixes (external review bugs 1-3): - processChargeGroup: aggregated refund key now hashes the sorted pending-row set (chargeID-square-agg-<sha256 suffix>) so a changed group can never mark a new row completed against an old smaller refund; >45-char chargeIDs use a hashed prefix instead of verbatim truncation (which would collide charges on Square's global key dedup). Same-set crash-retry keeps Square's dedup. - CreateTerminalPayment saved_card: two-tier idempotency key — client-supplied per-attempt UUID preferred (distinct identical charges no longer collapse), deterministic booking+type+amount+card fallback for no-key retry safety. PaymentModal sends a per-charge UUID cleared after success. - ensureRefundKey: legacy NULL-key manual refunds persist a generated key to the row BEFORE the Square call (race-safe AND idempotency_key IS NULL guard), so a lost-response retry reuses the key and never double-refunds. Wired into resumeManualPendingRefund and the sweep's manual-retry loop. Classification + money-safety hardening: - till.go/sweep.go: structured square.ErrorCode/IsNotFound are authoritative when present; message-substring matching only for non-structured errors (dev mock, client-side status errors). Fixes fragile string-matching driving sweep retries and gift-card clawbacks. - SaveCardForUser: ON CONFLICT (user_id, square_card_id) DO NOTHING + re-select (was a latent UNIQUE-violation 500 on save-card retry). - CreateBookingPayment: partial payments re-validated against remaining balance inside the advisory lock (closes concurrent-overpayment race). - InvalidateSquareCustomerCache on GDPR erasure paths (account.go, time-blockers.go stale-guest anonymization). - GetUserGiftCardBalanceAdmin: in-handler admin check (defense-in-depth). - getCheckoutHTTP: warn on multi-payment checkouts instead of dropping payments[1:]. - Cash/giftcard terminal branch: removed dead idempotency SELECT, "tip-" -> "till-" prefix. - UserPaymentModal: removed vestigial polling state; proper interval cleanup. - account/+page.svelte: gift-card redeem dialog links /terms. - nginx CSP: allow *.squarecdn.com and js.squareup.com so the Square Web Payments SDK + card iframe can tokenize behind the proxy. Tests: +8 regression tests covering changed-set refund keys, legacy NULL-key single-refund, saved-card client-key dedup/no-dedup, concurrent partials, and cache invalidation. Full suite + race detector clean via run-tests.sh lockfile.
26 lines
1.0 KiB
Bash
Executable File
26 lines
1.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# run-tests.sh — runs the backend test suite behind an advisory lockfile.
|
|
#
|
|
# WHY: every test package drops/recreates a FIXED test database name
|
|
# (crussell_test_handlers_payments, crussell_test_internal_square, etc.) in its
|
|
# TestMain. Two concurrent `go test` invocations targeting the same package
|
|
# therefore destroy each other's database mid-run (SQLSTATE 3D000 / 57P01
|
|
# "terminating connection due to administrator command"). This wrapper
|
|
# serializes runs on a flock(1) lockfile so the test DBs are never clobbered.
|
|
#
|
|
# USAGE:
|
|
# ./run-tests.sh [go test args...] # e.g. ./run-tests.sh -tags "test,dev" -count=1 -parallel 8 ./...
|
|
# LOCK_TIMEOUT=600 ./run-tests.sh ... # max wait for the lock (default 300s)
|
|
#
|
|
# Agents and humans: ALWAYS run backend tests through this script (or take the
|
|
# lockfile yourself: `flock /tmp/crussell-tests.lock -c '<cmd>'`).
|
|
|
|
set -u
|
|
|
|
LOCKFILE="${LOCKFILE:-/tmp/crussell-tests.lock}"
|
|
LOCK_TIMEOUT="${LOCK_TIMEOUT:-300}"
|
|
|
|
cd "$(dirname "$0")"
|
|
|
|
exec flock -w "$LOCK_TIMEOUT" "$LOCKFILE" go test "$@"
|