Test Cases: Unsubscribe Flow
/unsubscribe — NOT tenant-prefixed (no /rcme/unsubscribe). Email links carry ?token=…&memberId=…&tenantId=…&category=…; the SPA POSTs to /api/unsubscribe (and /api/resubscribe), both public. The token is a deterministic HMAC with no expiry. Valid categories: paymentReminders, eventReminders, membershipUpdates, broadcasts, all.
Module: Communications - Unsubscribe Flow
Backend Service: Email preferences service, unsubscribe token validation
Related Endpoints: POST /api/unsubscribe, POST /api/resubscribe, GET /api/email/unsubscribe/:token, PATCH /api/email/preferences
Priority: P2
Phase: 4
Prerequisites
- Email service configured and operational.
- Test member accounts with active email preferences.
- Access to email inbox to receive broadcast emails.
- Unsubscribe tokens generated from sent broadcasts.
- Email preference categories configured (broadcasts, paymentReminders, eventReminders, membershipUpdates).
Test Data
| Member | Initial Preferences | |
|---|---|---|
| Test Member 1 | member1@test.com | All categories subscribed |
| Test Member 2 | member2@test.com | Only broadcasts subscribed |
| Test Member 3 | member3@test.com | All categories unsubscribed |
| Expired Token Member | expired@test.com | Token > 30 days old |
Test Cases
TC-COMM-051: One-Click Unsubscribe (All Categories)
| Step | Action | Expected Result |
|---|---|---|
| 1 | Receive broadcast email as subscribed member. | Email in inbox with unsubscribe link in footer. |
| 2 | Click "Unsubscribe" link in email footer. | One-click unsubscribe page displayed. |
| 3 | Click "Unsubscribe from All". | API POST /api/unsubscribe called with token and no category. |
| 4 | Verify confirmation message. | "You have been unsubscribed from all emails" displayed. |
| 5 | Check member email preferences. | All preference categories set to OFF/false. |
| 6 | Send new broadcast to member's segment. | Member excluded from recipient list. |
| 7 | Verify member does NOT receive email. | No email delivered to unsubscribed member. |
TC-COMM-052: Unsubscribe by Specific Category
| Step | Action | Expected Result |
|---|---|---|
| 1 | Receive broadcast email. | Email in inbox. |
| 2 | Click "Unsubscribe" link. | Unsubscribe page displayed. |
| 3 | Select "Unsubscribe from Broadcasts only". | Category selection visible. |
| 4 | Confirm unsubscribe from broadcasts. | API POST /api/unsubscribe called with category: "broadcasts". |
| 5 | Verify confirmation. | "You have been unsubscribed from broadcasts" displayed. |
| 6 | Check email preferences. | Only broadcasts set to OFF; other categories unchanged. |
| 7 | Trigger payment reminder email. | Member receives payment reminder (still subscribed). |
| 8 | Send broadcast email. | Member does NOT receive broadcast. |
TC-COMM-053: Unsubscribe from Event Reminders
| Step | Action | Expected Result |
|---|---|---|
| 1 | Access unsubscribe page via event reminder email. | Unsubscribe page displayed. |
| 2 | Select "Unsubscribe from Event Reminders". | Category selection made. |
| 3 | Confirm. | API called with category: "eventReminders". |
| 4 | Verify preferences. | eventReminders OFF; other categories unchanged. |
| 5 | Trigger event reminder. | Member does NOT receive event reminder. |
TC-COMM-054: Unsubscribe from Payment Reminders
| Step | Action | Expected Result |
|---|---|---|
| 1 | Access unsubscribe page via payment reminder email. | Unsubscribe page displayed. |
| 2 | Select "Unsubscribe from Payment Reminders". | Category selection made. |
| 3 | Confirm. | API called with category: "paymentReminders". |
| 4 | Verify preferences. | paymentReminders OFF; other categories unchanged. |
| 5 | Trigger payment reminder. | Member does NOT receive payment reminder. |
TC-COMM-055: Unsubscribe Link Token Validation - Valid Token
| Step | Action | Expected Result |
|---|---|---|
| 1 | Send broadcast email to member. | Email sent with unique unsubscribe token. |
| 2 | Extract unsubscribe token from email link. | Token embedded in URL. |
| 3 | Call GET /api/email/unsubscribe/:token. | API validates token. |
| 4 | Verify response. | 200 OK with member info and preferences. |
| 5 | Use token to unsubscribe. | Unsubscribe successful. |
TC-COMM-056: Invalid Unsubscribe Token
| Step | Action | Expected Result |
|---|---|---|
| 1 | Navigate to unsubscribe URL with invalid token. | e.g., /unsubscribe?token=invalid123. |
| 2 | Verify API response. | 400 Bad Request or 404 Not Found. |
| 3 | Verify error page displayed. | "Invalid unsubscribe link" message shown. |
| 4 | Verify no preferences changed. | Member preferences remain unchanged. |
| 5 | Provide alternative action. | Link to login and manage preferences manually. |
TC-COMM-057: Expired Unsubscribe Token
| Step | Action | Expected Result |
|---|---|---|
| 1 | Use unsubscribe token older than 30 days. | Token has expiration. |
| 2 | Navigate to unsubscribe page. | API validates token age. |
| 3 | Verify response. | 400 Bad Request with TOKEN_EXPIRED error. |
| 4 | Verify user-friendly message. | "This unsubscribe link has expired" displayed. |
| 5 | Provide alternative. | Option to request new unsubscribe link or login. |
TC-COMM-058: Unsubscribe Confirmation Page Display
| Step | Action | Expected Result |
|---|---|---|
| 1 | Click unsubscribe link from email. | Page loads. |
| 2 | Verify page elements. | Organization branding displayed. |
| 3 | Verify member identification. | Member email displayed (partial/masked). |
| 4 | Verify options displayed. | "Unsubscribe All", category options, "Manage Preferences" link. |
| 5 | Verify mobile responsiveness. | Page displays correctly on mobile device. |
TC-COMM-059: Resubscribe Flow - Single Category
| Step | Action | Expected Result |
|---|---|---|
| 1 | Member previously unsubscribed from broadcasts. | broadcasts preference is OFF. |
| 2 | Access email preferences page (logged in). | Preferences displayed. |
| 3 | Toggle "Broadcasts" to ON. | Checkbox/toggle enabled. |
| 4 | Save preferences. | API PATCH /api/email/preferences called. |
| 5 | Verify confirmation. | "Preferences updated" message. |
| 6 | Verify database update. | broadcasts preference is ON. |
| 7 | Send broadcast. | Member receives broadcast email. |
TC-COMM-060: Resubscribe Flow - Via Public Endpoint
| Step | Action | Expected Result |
|---|---|---|
| 1 | Member is fully unsubscribed. | All preferences OFF. |
| 2 | Member requests resubscription (support request). | Token generated. |
| 3 | Call POST /api/resubscribe with token. | API processes resubscription. |
| 4 | Verify response. | 200 OK with updated preferences. |
| 5 | Verify all preferences restored. | All categories set to ON. |
| 6 | Verify member receives future emails. | Emails delivered successfully. |
TC-COMM-061: Resubscribe Specific Categories Only
| Step | Action | Expected Result |
|---|---|---|
| 1 | Member unsubscribed from all categories. | All preferences OFF. |
| 2 | Access preferences page. | Current preferences shown (all OFF). |
| 3 | Enable only "Membership Updates" and "Payment Reminders". | Selective toggle. |
| 4 | Save preferences. | API called with partial preferences. |
| 5 | Verify selective subscription. | Only selected categories ON. |
| 6 | Verify email behavior. | Receives membership/payment emails, not broadcasts. |
TC-COMM-062: Manage Preferences Option from Unsubscribe Page
| Step | Action | Expected Result |
|---|---|---|
| 1 | Click unsubscribe link from email. | Unsubscribe page displayed. |
| 2 | Click "Manage Preferences" link. | Redirected to login or preferences page. |
| 3 | If not logged in, authenticate. | Login required for full preferences. |
| 4 | Access full preferences page. | All email categories with toggles displayed. |
| 5 | Make granular changes. | Individual category toggles. |
| 6 | Save and verify. | All changes persisted correctly. |
TC-COMM-063: Unsubscribe - Already Unsubscribed Member
| Step | Action | Expected Result |
|---|---|---|
| 1 | Member already unsubscribed from all. | Preferences all OFF. |
| 2 | Click old unsubscribe link from previous email. | Page loads with valid token. |
| 3 | Attempt to unsubscribe again. | Graceful handling. |
| 4 | Verify message. | "You are already unsubscribed" or confirmation without error. |
| 5 | Verify preferences unchanged. | Still all OFF. |
TC-COMM-064: Unsubscribe Token - One-Time Use Enforcement
| Step | Action | Expected Result |
|---|---|---|
| 1 | Use unsubscribe token to unsubscribe. | Successful unsubscription. |
| 2 | Attempt to use same token again. | Token may be single-use or multi-use. |
| 3 | If single-use, verify error. | "Token already used" or similar. |
| 4 | If multi-use, verify idempotent behavior. | Same result without error. |
TC-COMM-065: Unsubscribe Link Format in Email
| Step | Action | Expected Result |
|---|---|---|
| 1 | Receive broadcast email. | Email delivered. |
| 2 | Inspect email footer. | Unsubscribe link visible. |
| 3 | Verify link text. | Clear "Unsubscribe" or "Manage preferences" text. |
| 4 | Verify link URL structure. | Contains organization domain and token. |
| 5 | Verify link is clickable. | Not broken or malformed. |
| 6 | Verify compliance. | Meets CAN-SPAM / GDPR requirements. |
TC-COMM-066: Unsubscribe Does Not Affect System Notifications
| Step | Action | Expected Result |
|---|---|---|
| 1 | Member unsubscribes from all marketing emails. | All marketing preferences OFF. |
| 2 | Trigger transactional email (password reset). | System email sent. |
| 3 | Verify member receives transactional email. | Password reset email delivered. |
| 4 | Trigger membership renewal confirmation. | System notification sent. |
| 5 | Verify receipt. | Critical system emails still delivered. |
TC-COMM-067: Unsubscribe Statistics Tracking
| Step | Action | Expected Result |
|---|---|---|
| 1 | Send broadcast to 100 members. | Broadcast delivered. |
| 2 | 5 members click unsubscribe. | Unsubscribe events recorded. |
| 3 | View broadcast statistics. | Unsubscribe count visible. |
| 4 | Verify unsubscribe rate. | (5/100) * 100 = 5% unsubscribe rate. |
| 5 | Verify individual tracking. | Each unsubscriber identified in report. |
TC-COMM-068: Bulk Unsubscribe via Admin
| Step | Action | Expected Result |
|---|---|---|
| 1 | Admin accesses member management. | Member list displayed. |
| 2 | Select multiple members. | Bulk selection enabled. |
| 3 | Apply "Unsubscribe from all" action. | Bulk action initiated. |
| 4 | Confirm action. | Warning about bulk operation. |
| 5 | Verify all selected members unsubscribed. | Preferences updated for all. |
| 6 | Verify audit log. | Bulk unsubscribe action logged. |
API Test Cases
TC-COMM-API-015: POST /api/unsubscribe - Unsubscribe All
Request: POST /api/unsubscribe
{
"token": "valid_unsubscribe_token_abc123"
}
Expected Response: 200 OK
{
"success": true,
"message": "Successfully unsubscribed from all email communications",
"memberId": "MEM-2024-001",
"unsubscribedCategories": ["broadcasts", "eventReminders", "paymentReminders", "membershipUpdates"],
"timestamp": "2024-01-15T10:30:00Z"
}
TC-COMM-API-016: POST /api/unsubscribe - Unsubscribe by Category
Request: POST /api/unsubscribe
{
"token": "valid_unsubscribe_token_abc123",
"category": "broadcasts"
}
Expected Response: 200 OK
{
"success": true,
"message": "Successfully unsubscribed from broadcasts",
"memberId": "MEM-2024-001",
"unsubscribedCategories": ["broadcasts"],
"remainingSubscriptions": ["eventReminders", "paymentReminders", "membershipUpdates"],
"timestamp": "2024-01-15T10:35:00Z"
}
TC-COMM-API-017: POST /api/unsubscribe - Invalid Token
Request: POST /api/unsubscribe
{
"token": "invalid_token_xyz"
}
Expected Response: 400 Bad Request
{
"error": "INVALID_TOKEN",
"message": "The unsubscribe token is invalid or malformed"
}
TC-COMM-API-018: POST /api/unsubscribe - Expired Token
Request: POST /api/unsubscribe
{
"token": "expired_token_30_days_old"
}
Expected Response: 400 Bad Request
{
"error": "TOKEN_EXPIRED",
"message": "This unsubscribe link has expired. Please login to manage your preferences."
}
TC-COMM-API-019: POST /api/unsubscribe - Invalid Category
Request: POST /api/unsubscribe
{
"token": "valid_token",
"category": "invalidCategory"
}
Expected Response: 400 Bad Request
{
"error": "INVALID_CATEGORY",
"message": "Invalid email category specified"
}
TC-COMM-API-020: GET /api/email/unsubscribe/:token - Validate Token
Request: GET /api/email/unsubscribe/valid_token_abc123
Expected Response: 200 OK
{
"valid": true,
"memberEmail": "m*r@example.com",
"currentPreferences": {
"broadcasts": true,
"eventReminders": true,
"paymentReminders": true,
"membershipUpdates": true
},
"tokenExpiresAt": "2024-02-15T10:30:00Z"
}
TC-COMM-API-021: POST /api/resubscribe - Resubscribe All
Request: POST /api/resubscribe
{
"token": "resubscribe_token_abc123"
}
Expected Response: 200 OK
{
"success": true,
"message": "Successfully resubscribed to email communications",
"memberId": "MEM-2024-001",
"subscribedCategories": ["broadcasts", "eventReminders", "paymentReminders", "membershipUpdates"],
"timestamp": "2024-01-16T09:00:00Z"
}
TC-COMM-API-022: POST /api/resubscribe - Resubscribe Specific Category
Request: POST /api/resubscribe
{
"token": "resubscribe_token_abc123",
"category": "broadcasts"
}
Expected Response: 200 OK
{
"success": true,
"message": "Successfully resubscribed to broadcasts",
"memberId": "MEM-2024-001",
"subscribedCategories": ["broadcasts"],
"timestamp": "2024-01-16T09:15:00Z"
}
TC-COMM-API-023: PATCH /api/email/preferences - Update Preferences (Authenticated)
Auth Required: Yes
Request: PATCH /api/email/preferences
{
"paymentReminders": true,
"eventReminders": false,
"membershipUpdates": true,
"broadcasts": false
}
Expected Response: 200 OK
{
"success": true,
"message": "Email preferences updated",
"preferences": {
"paymentReminders": true,
"eventReminders": false,
"membershipUpdates": true,
"broadcasts": false
},
"updatedAt": "2024-01-16T10:00:00Z"
}
TC-COMM-API-024: GET /api/email/preferences - Get Current Preferences
Auth Required: Yes
Request: GET /api/email/preferences
Expected Response: 200 OK
{
"memberId": "MEM-2024-001",
"email": "member@example.com",
"preferences": {
"paymentReminders": true,
"eventReminders": true,
"membershipUpdates": true,
"broadcasts": true
},
"lastUpdated": "2024-01-10T14:30:00Z",
"globalUnsubscribe": false
}
Edge Cases & Error Handling
TC-COMM-069: Unsubscribe with Missing Token
| Step | Action | Expected Result |
|---|---|---|
| 1 | Call POST /api/unsubscribe without token. | - |
| 2 | Verify response. | 400 Bad Request - "Token is required". |
TC-COMM-070: Unsubscribe - Member Account Deleted
| Step | Action | Expected Result |
|---|---|---|
| 1 | Member account was deleted after email sent. | Token still exists. |
| 2 | Click unsubscribe link from old email. | Token validation. |
| 3 | Verify graceful handling. | "Member not found" or silent success. |
TC-COMM-071: Concurrent Unsubscribe Requests
| Step | Action | Expected Result |
|---|---|---|
| 1 | Open unsubscribe link in two browser tabs. | Two sessions active. |
| 2 | Click unsubscribe in both tabs simultaneously. | Race condition test. |
| 3 | Verify no errors. | Both requests handled gracefully. |
| 4 | Verify final state. | Member is unsubscribed (idempotent). |
TC-COMM-072: Unsubscribe Page - Session Timeout
| Step | Action | Expected Result |
|---|---|---|
| 1 | Open unsubscribe page. | Page displayed. |
| 2 | Wait extended period without action. | Session may timeout. |
| 3 | Click unsubscribe. | Either works or refreshes token. |
| 4 | Verify user experience. | Clear error or success message. |
Security Test Cases
TC-COMM-073: Token Enumeration Prevention
| Step | Action | Expected Result |
|---|---|---|
| 1 | Attempt to enumerate tokens via API. | Sequential token guessing. |
| 2 | Verify rate limiting. | Requests throttled after threshold. |
| 3 | Verify response consistency. | Same error for invalid/nonexistent tokens. |
TC-COMM-074: Cross-Member Token Usage
| Step | Action | Expected Result |
|---|---|---|
| 1 | Member A obtains Member B's unsubscribe token. | Token shared/leaked. |
| 2 | Member A attempts to use Member B's token. | Token processed. |
| 3 | Verify only Member B affected. | Token tied to specific member. |
| 4 | Verify no additional member data exposed. | Email masked in responses. |
TC-COMM-075: SQL Injection in Token Parameter
| Step | Action | Expected Result |
|---|---|---|
| 1 | Submit malicious token: ' OR '1'='1. | Injection attempt. |
| 2 | Verify sanitization. | Token rejected as invalid. |
| 3 | Verify no database errors. | Clean error handling. |
Accessibility Test Cases
TC-COMM-076: Unsubscribe Page Accessibility
| Step | Action | Expected Result |
|---|---|---|
| 1 | Navigate unsubscribe page with screen reader. | All elements announced. |
| 2 | Verify keyboard navigation. | Tab through all interactive elements. |
| 3 | Verify button/link focus states. | Clear visual focus indicators. |
| 4 | Verify color contrast. | WCAG AA compliant. |
| 5 | Verify form labels. | All inputs properly labeled. |