jam-cloud/web/ai/tasks/test-subscriptions-plan.md

77 lines
3.7 KiB
Markdown
Raw Normal View History

2026-03-03 03:03:43 +00:00
# Subscription/Recurly Test Plan (Rails 8 Branch)
## Objective
Close subscription lifecycle regressions by adding executable tests in `web` and `ruby` that do not depend on live Recurly network calls.
## What Exists
- Webhook coverage exists (request + model) and is active.
- Core subscription API and sync logic coverage exists historically, but major suites are disabled (`xdescribe`).
- Active live Recurly integration coverage now exists in (opt-out via `SKIP_LIVE_RECURLY=1`):
- `ruby/spec/jam_ruby/integration/recurly_live_integration_spec.rb`
## Priority 1: API Subscription Lifecycle (`web/spec/requests`)
Add request specs for `ApiRecurlyController` with `RecurlyClient` stubbed.
### 1.1 Plan-first then payment
- `POST /api/recurly/change_subscription` with paid plan and no billing info:
- expect desired plan is set and response indicates payment method still needed.
- `POST /api/recurly/update_payment` with token:
- expect `handle_create_subscription` called with users desired plan.
- expect success payload includes plan metadata and `has_billing_info = true`.
### 1.2 Payment-first then plan
- `POST /api/recurly/update_payment` first (no desired plan yet):
- expect no failure and billing info persisted.
- Then `POST /api/recurly/change_subscription` to paid plan:
- expect desired plan update call and success response.
### 1.3 Negative paths
- `change_subscription` with unchanged plan:
- expect 422 and `No change made to plan`.
- `update_payment` when client raises `RecurlyClientError`:
- expect 404 + serialized error payload.
## Priority 2: Sync Decision Tree (`ruby/spec/jam_ruby/models`)
Add focused unit specs for `RecurlyClient#sync_subscription` with Recurly API mocked.
### 2.1 Unchanged/good-standing path
- account exists, not past_due, active subscription, desired == effective.
- expect sync code `good_standing_unchanged` and no plan mutation.
### 2.2 Canceled/expired path
- no active subscription (or expired).
- expect effective plan set to free (`nil`) and sync code `no_subscription_or_expired`.
### 2.3 Past-due path
- account `has_past_due_invoice = true`.
- expect effective plan dropped to free and sync code `is_past_due_changed`.
### 2.4 Trial and free-month behavior
- user in trial with desired gold and active account.
- verify trial code path + post-trial behavior once time advances (use `Timecop` or `travel_to` depending what is stable in this suite).
## Priority 3: Hourly Job Integration (`ruby/spec/jam_ruby/resque` or model-level)
- Validate `JamRuby::HourlyJob.perform` triggers `User.hourly_check`.
- Validate `User.subscription_sync` only selects intended users and calls client sync.
- Validate `User.subscription_transaction_sync` advances `GenericState.recurly_transactions_last_sync_at` using mocked transaction stream.
## Priority 4: Browser Coverage (`web/spec/features`)
Add high-value feature coverage for subscription management screen:
- plan-first flow shows payment-needed state and routes to update payment.
- payment-first flow then selecting plan results in active paid subscription state.
Use stubs/fakes around `RecurlyClient` in feature env to avoid external dependency.
## Execution Order
1. Request specs for `ApiRecurlyController` (fast, high signal).
2. `RecurlyClient#sync_subscription` specs (logic-heavy, deterministic).
3. Hourly sync integration coverage.
4. Browser feature tests for UX flow parity.
## Definition of Done
- New tests are active (not `xdescribe`/commented out).
- Both plan-first and payment-first lifecycles are covered.
- Hourly sync scenarios for unchanged, canceled/expired, and past-due are covered.
- First-free-month/gold behavior has explicit time-based assertions.
- `docs/dev/subscriptions.md` updated with links to new spec files and scenarios.