Skip to content

docs: SSP Essentials manual E2E integration testing guide - #217

Open
iloveagent57 wants to merge 1 commit into
mainfrom
adusenbery/ssp-essentials-integration-testing-doc
Open

docs: SSP Essentials manual E2E integration testing guide#217
iloveagent57 wants to merge 1 commit into
mainfrom
adusenbery/ssp-essentials-integration-testing-doc

Conversation

@iloveagent57

Copy link
Copy Markdown
Member

Summary

  • Adds docs/customer-billing/ssp-essentials-integration-testing.md for contractor devs testing the SSP checkout and Braze email flow
  • Covers prerequisites (settings/private.py, CELERY_ALWAYS_EAGER, DB seed), happy path walkthrough (invoice.paid -> send_payment_receipt_email), signup confirmation email (provisioning-triggered, not Stripe), Stripe test clock usage for time-based events, and a task/event/settings reference table
  • Notes that Braze tasks will branch on SspProduct (not yet implemented) and directs readers to ask for essentials campaign UUIDs once created

Test plan

  • Doc renders correctly in GitHub
  • Links and code blocks are valid

🤖 Generated with Claude Code

@iloveagent57
iloveagent57 requested review from a team as code owners June 26, 2026 14:18
Copilot AI review requested due to automatic review settings June 26, 2026 14:18
Covers Stripe sandbox and Braze dev workspace testing for the SSP
checkout and email notification flow, including happy path walkthrough,
test clock usage for time-based events, and a task/event/settings
reference table.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@iloveagent57
iloveagent57 force-pushed the adusenbery/ssp-essentials-integration-testing-doc branch from 35a0a22 to 07aa99e Compare June 26, 2026 14:20

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new internal guide for manually validating the SSP Essentials checkout + email notification flow end-to-end against Stripe test mode and the Braze dev workspace, focusing on replaying Stripe events locally and verifying the resulting Celery/Braze behavior.

Changes:

  • Introduces a new manual E2E integration testing doc for SSP Essentials billing emails.
  • Documents prerequisites (local seed data, Stripe/Braze keys, Celery execution mode) and a payment-receipt happy path.
  • Adds guidance for non-Stripe-triggered signup confirmation emails, Stripe test clocks, and a task/event/settings reference.


## Prerequisites

**Devstack.** The app server must be running. Email tasks are async Celery tasks; by default you also need the worker running (`make dev.celery` or equivalent). If running a separate worker container is inconvenient, add `CELERY_ALWAYS_EAGER = True` to `settings/private.py` to execute tasks synchronously inline instead.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Valid comment, see devstack.py#L73


**Local DB seed.** You need at least one active `SspProduct` record pointing at a valid Stripe price lookup key, and a test user in the LMS with an enterprise admin role for a test enterprise. `get_enterprise_admins` fetches LMS enterprise data to build Braze recipients; if it returns empty, the task fails before reaching Braze.

**`settings/private.py`.** Add the following (create the file if it doesn't exist -- it's gitignored):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: People can probably figure it out, but it doesn't hurt to be explicit.

STRIPE_API_KEY = 'sk_test_...'

# Run Celery tasks inline without a separate worker (optional but convenient locally)
CELERY_ALWAYS_EAGER = True

**Devstack.** The app server must be running. Email tasks are async Celery tasks; by default you also need the worker running (`make dev.celery` or equivalent). If running a separate worker container is inconvenient, add `CELERY_ALWAYS_EAGER = True` to `settings/private.py` to execute tasks synchronously inline instead.

**Local DB seed.** You need at least one active `SspProduct` record pointing at a valid Stripe price lookup key, and a test user in the LMS with an enterprise admin role for a test enterprise. `get_enterprise_admins` fetches LMS enterprise data to build Braze recipients; if it returns empty, the task fails before reaching Braze.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


**2. Complete checkout in Stripe.** Open the checkout URL from the response in a browser. Use test card `4242 4242 4242 4242`, any future expiry, any CVC. Stripe creates a subscription and immediately generates a paid invoice for $0 (trial).

**3. Note the current timestamp.** Grab a Unix timestamp from a minute before you started (e.g. `date -v-2M +%s` on Mac). This is your `--since` value to avoid pulling unrelated older events.

@marlonkeating marlonkeating Jun 26, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines +79 to +81
python manage.py fetch_and_handle_stripe_events \
--event-types invoice.created invoice.paid \
--since <timestamp>

@marlonkeating marlonkeating Jun 26, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


**Stale price cache.** If you add or change products in the sandbox, `get_all_stripe_prices` returns cached data until TTL expires. Clear it from a Django shell: `from edx_django_utils.cache import TieredCache; TieredCache.dangerous_clear_all_tiers()`.

**`invoice.paid` before `invoice.created`.** `send_payment_receipt_email` requires a `StripeEventSummary` record created by the `invoice.created` handler. Include both event types in `--event-types` when replaying.

## Common failure modes

**Worker not running.** Tasks queue silently; nothing sends, no error in the management command output. Start the worker or set `CELERY_ALWAYS_EAGER = True` in `private.py`.
Copilot AI review requested due to automatic review settings June 26, 2026 14:23
@codecov

codecov Bot commented Jun 26, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 86.58%. Comparing base (1ac224a) to head (07aa99e).

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #217   +/-   ##
=======================================
  Coverage   86.58%   86.58%           
=======================================
  Files         155      155           
  Lines       12953    12953           
  Branches     1239     1239           
=======================================
  Hits        11215    11215           
  Misses       1423     1423           
  Partials      315      315           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated 4 comments.

Comment on lines +14 to +18
**Devstack.** The app server must be running. Email tasks are async Celery tasks; by default you also need the worker running (`make dev.celery` or equivalent). If running a separate worker container is inconvenient, add `CELERY_ALWAYS_EAGER = True` to `settings/private.py` to execute tasks synchronously inline instead.

**Local DB seed.** You need at least one active `SspProduct` record pointing at a valid Stripe price lookup key, and a test user in the LMS with an enterprise admin role for a test enterprise. `get_enterprise_admins` fetches LMS enterprise data to build Braze recipients; if it returns empty, the task fails before reaching Braze.

**`settings/private.py`.** Add the following (create the file if it doesn't exist -- it's gitignored):
Comment on lines +21 to +24
# Stripe sandbox -- test mode secret key
# Developers -> API keys in the sandbox dashboard:
# https://dashboard.stripe.com/acct_1RtEfJQ60jNALKNU/test/dashboard
STRIPE_API_KEY = 'sk_test_...'

@marlonkeating marlonkeating Jun 26, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the one hand, we know our exact sandbox, so the url here helps save a step in finding it. On the other hand, enterprise-access is a publicly-viewable repository so we would be exposing an internal stripe id to potential attackers. Will leave it to your judgment which way to go.


**2. Complete checkout in Stripe.** Open the checkout URL from the response in a browser. Use test card `4242 4242 4242 4242`, any future expiry, any CVC. Stripe creates a subscription and immediately generates a paid invoice for $0 (trial).

**3. Note the current timestamp.** Grab a Unix timestamp from a minute before you started (e.g. `date -v-2M +%s` on Mac). This is your `--since` value to avoid pulling unrelated older events.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

date -v-2M +%s is BSD date (macOS only). Linux equivalent is date -d '2 minutes ago' +%s. Since --created-since-hours-ago removes the need for a timestamp entirely, the simplest fix is to drop the date example and use that flag instead.

Comment on lines +121 to +122
| Task | Triggering Stripe event | `private.py` settings key |
|---|---|---|

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Again people can probably figure this out but doesn't hurt to be explicit

@marlonkeating marlonkeating left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed with Claude's help


## Signup confirmation email

`send_enterprise_provision_signup_confirmation_email` fires at the end of the provisioning workflow (`provisioning/models.py`), not via a Stripe event. To test it, either trigger the full provisioning workflow or call the task directly from a Django shell:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: It would be nice to document triggering the full provisioning workflow, especially locally if there's a simple-ish way to do that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants