8. Testing Strategy
8.1 Test Stack
| Type | Tool | Coverage Target | Runs In |
|---|---|---|---|
| Unit Tests | Jest | 80% minimum | CI — every push |
| Integration Tests | Jest + Supertest | All custom endpoints | CI — every push |
| E2E Tests | Playwright | All critical user journeys | Staging deploy |
| Smoke Tests | Playwright (subset) | 5–10 critical paths | Post prod deploy |
8.2 Critical Test Scenarios
Download System
✓ Valid token → presigned URL returned, download_count incremented
✗ Expired token → 410 DOWNLOAD_EXPIRED
✗ Exhausted token (3/3) → 410 DOWNLOAD_LIMIT_REACHED
✗ Wrong customer token → 403 FORBIDDEN ← IDOR prevention
✗ Tampered UUID → 404 NOT_FOUNDInvite Flow
✓ Create invite → email sent → token in DB with status: pending
✗ Duplicate invite for same email → 409 INVITE_ALREADY_EXISTS
✗ Expired invite clicked → 410 INVITE_EXPIRED
✗ Already accepted token → 409 INVITE_ALREADY_ACCEPTED
✓ Full flow → customer created → JWT returned → redirect to /Purchase Flow
✓ Add to cart → checkout → payment success → download links created → email sent
✗ Payment failure → order status = failed, no download links created
✗ Price change at checkout → recalculated correctly from Medusa cart
✗ Stripe webhook duplicate → idempotency check prevents double fulfilmentAuthentication
✓ Login success → JWT in cookie, redirect to /
✗ Login failure × 5 → account locked for 30 minutes
✗ Expired JWT → redirect to /login?redirect={path}
✗ Protected route without auth → redirect to /login8.3 Example Tests
// Download IDOR test
it('should reject download token belonging to another customer', async () => {
const { token } = await createDownloadLink({ customerId: customer1.id })
const res = await request(app)
.get(`/store/downloads/${token}`)
.set('Authorization', `Bearer ${customer2.jwt}`)
expect(res.status).toBe(403)
expect(res.body.code).toBe('FORBIDDEN')
})// Invite duplicate test
it('should reject duplicate invite for same email', async () => {
await createInvite({ email: 'staff@vespertene.com' })
const res = await request(app)
.post('/admin/invites')
.set('Authorization', `Bearer ${adminJwt}`)
.send({ email: 'staff@vespertene.com' })
expect(res.status).toBe(409)
expect(res.body.code).toBe('INVITE_ALREADY_EXISTS')
})8.4 E2E Test Journeys (Playwright)
// Full invite journey
test('invite → register → access store', async ({ page }) => {
// Admin creates invite
await adminCreateInvite('newstaff@vespertene.com')
// Staff clicks invite link from email
await page.goto(`/invite/${token}`)
await expect(page.locator('input[name=email]')).toHaveValue('newstaff@vespertene.com')
// Staff fills form
await page.fill('input[name=first_name]', 'Alex')
await page.fill('input[name=last_name]', 'Smith')
await page.fill('input[name=password]', 'SecurePass123')
await page.click('button[type=submit]')
// Should be logged in and redirected to homepage
await expect(page).toHaveURL('/')
await expect(page.locator('[data-testid=customer-menu]')).toBeVisible()
})Smoke tests run against production after every deploy. They cover only the 5–10 most critical paths and must complete in under 2 minutes.