Skip to content

Commit 1a11604

Browse files
test(e2e): Wave 4b — UI-driven Razorpay TEST-card payment E2E (free → upgrade → Pro) (#194)
* test(e2e): Wave 4b — UI-driven Razorpay TEST-card payment E2E (free → upgrade → Pro) The CEO ask: "There is no CI that actually tries to create a test user, then tries to upgrade and enter test details and check everything is working fine." This adds the real UI loop. Design: docs/ci/01-CI-INTEGRATION-DESIGN.md §"Razorpay test-card payment E2E" (Approach A) + the api Wave 4b PR (cohort test-mode rzp_test_* checkout routing). e2e/live-ui-payment.spec.ts — TWO tests, two lanes: 1. @pr-smoke CONTRACT-ONLY (PR-able, no card entry): mint a free cohort user via the factory → drive the dashboard /app/checkout?plan=pro UI → createCheckout → assert the SPA reaches a Razorpay checkout URL, OR the inert cohort path (synthetic_test_cohort 403, when rzp_test_* keys aren't wired), OR the honest billing-not-configured fallback. All three prove the UI→api→checkout wiring end-to-end against the REAL api. Reaped. 2. FULL card-entry (nightly only, gated on E2E_RAZORPAY_TEST_MODE=1): same up to the Razorpay page, then fill the subscription test card 4718 6091 0820 4366 + OTP 1234 (cross-origin iframes + popup handled), submit → poll /api/v1/capabilities for tier=pro (the real TEST-mode webhook drives the upgrade). RESILIENT + SOFT-FAILS (skip-with-reason) on a Razorpay markup change or an unreachable webhook so a UI change never reds the nightly. Deterministic upgrade is covered by the api webhook-injection suite. Wiring: the spec matches live-*.spec.ts so it runs in e2e-prod.yml (30-min) + e2e-live.yml (nightly staging); the @pr-smoke leg runs per-PR via e2e-pr-smoke.yml. Both nightly workflows now pass E2E_RAZORPAY_TEST_MODE from a repo var (inert until the operator wires test keys) + arm the factory token. Gating: skips clean until E2E_LIVE/E2E_ACCOUNT_TOKEN (and, for the card leg, E2E_RAZORPAY_TEST_MODE) are set. Verified live: the @pr-smoke contract-only test PASSES against prod api.instanode.dev (minted free cohort → UI → reached the inert cohort path, reaped). npm run gate green (tsc + build + 1129 vitest). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(e2e): anchor Razorpay host check — fix CodeQL js/regex/missing-regexp-anchor CodeQL flagged the unanchored /(razorpay\.com|rzp\.io)/i regex used to validate a reached checkout URL: an unanchored substring match means a hostile URL like https://evil.com/?x=razorpay.com would pass. Replace the assertion with a parsed isRazorpayCheckoutHost() that inspects the URL hostname (exact apex or subdomain match), and anchor the Playwright route() regex on scheme + exact host segment. No security weakening — strictly tightens the host check. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent fec01d6 commit 1a11604

3 files changed

Lines changed: 572 additions & 0 deletions

File tree

.github/workflows/e2e-live.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,15 @@ jobs:
104104
# self-skip loudly; the contract-only legs (OAuth state-replay, CORS,
105105
# CLI canonical host, magic-link start, tampered-token) still run.
106106
E2E_JWT_SECRET: ${{ secrets.E2E_JWT_SECRET }}
107+
# Wave 4b: arm the FULL Razorpay TEST-card payment leg in
108+
# live-ui-payment.spec.ts. INERT until the operator wires rzp_test_*
109+
# keys on the staging api AND sets vars.E2E_RAZORPAY_TEST_MODE=1 —
110+
# otherwise the card-entry test skips clean. See
111+
# docs/ci/01-CI-INTEGRATION-DESIGN.md §"Razorpay test-card payment".
112+
E2E_RAZORPAY_TEST_MODE: ${{ vars.E2E_RAZORPAY_TEST_MODE }}
113+
# The payment spec mints its OWN free cohort account via the factory;
114+
# arm it with the mint-guard token so it doesn't skip-on-unarmed.
115+
E2E_ACCOUNT_TOKEN: ${{ secrets.E2E_ACCOUNT_TOKEN }}
107116
run: npm run test:e2e:live
108117

109118
# Reaper ALWAYS runs (even on test failure/cancel) so a leaked resource

.github/workflows/e2e-prod.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,13 @@ jobs:
160160
# X-E2E-Test-Token header skips the per-fingerprint cap when it matches
161161
# this secret (api internal/middleware/fingerprint.go).
162162
E2E_TEST_TOKEN: ${{ secrets.E2E_TEST_TOKEN }}
163+
# Wave 4b: arm the FULL Razorpay TEST-card payment leg in
164+
# live-ui-payment.spec.ts. Sourced from a repo VARIABLE so it is
165+
# INERT until the operator wires rzp_test_* keys on the api AND flips
166+
# vars.E2E_RAZORPAY_TEST_MODE=1. Until then the card-entry test
167+
# skips clean; the @pr-smoke contract-only leg always runs. See
168+
# docs/ci/01-CI-INTEGRATION-DESIGN.md §"Razorpay test-card payment".
169+
E2E_RAZORPAY_TEST_MODE: ${{ vars.E2E_RAZORPAY_TEST_MODE }}
163170
run: npm run test:e2e:live
164171

165172
- name: Reap minted account (teardown)

0 commit comments

Comments
 (0)