Specification
8. Testing

8. Testing Strategy

8.1 Test Stack

TypeToolCoverage TargetRuns In
Unit TestsJest80% minimumCI — every push
Integration TestsJest + SupertestAll custom endpointsCI — every push
E2E TestsPlaywrightAll critical user journeysStaging deploy
Smoke TestsPlaywright (subset)5–10 critical pathsPost 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_FOUND

Invite 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 fulfilment

Authentication

✓ 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 /login

8.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.