Messages + Comments — QA Plan
1. DM send
| # | Scenario | Expected |
|---|---|---|
| 1.1 | Coach DMs member with text only | 201; row inserted; WS event emitted to both rooms; push enqueued |
| 1.2 | Member DMs coach with text | 201 |
| 1.3 | Member DMs another member | 403 |
| 1.4 | Coach DMs another coach | 201 |
| 1.5 | Send with no content and no attachments | 400 |
| 1.6 | Send to a deactivated member’s membership | 404 |
| 1.7 | Send to a recipient in another org | 403 |
2. Attachments
| # | Scenario | Expected |
|---|---|---|
| 2.1 | Get upload URL → PUT image → reference in send | Message appears with image thumbnail |
| 2.2 | Send referencing a non-existent uploadId | 400 / 404 from upload resolver |
| 2.3 | Send referencing someone else’s upload | 403 |
3. Conversation list + history
| # | Scenario | Expected |
|---|---|---|
| 3.1 | List conversations | One entry per other-participant with last message + unread count |
| 3.2 | History pagination by cursor | Older messages return on subsequent calls |
| 3.3 | Soft-deleted messages absent | Filtered |
4. Read receipts
| # | Scenario | Expected |
|---|---|---|
| 4.1 | Recipient marks single read | read_at set; sender receives WS message:read |
| 4.2 | Recipient bulk-marks conversation | All unread for them flip in one shot |
| 4.3 | Sender tries to mark own message read | 403 (only recipient can) |
5. Workout-anchored comments
| # | Scenario | Expected |
|---|---|---|
| 5.1 | Coach posts on a member’s assignment | 201; message row has workout_assignment_id |
| 5.2 | Member posts on own assignment | 201 |
| 5.3 | Other member posts on another’s assignment | 403 |
| 5.4 | Soft-delete a comment | Hidden |
| 5.5 | Workout assignment is deleted | Comments CASCADE-deleted (hard) |
| 5.6 | Program-scoped unread count | Matches DB truth |
6. WebSocket
| # | Scenario | Expected |
|---|---|---|
| 6.1 | Connect with valid Clerk JWT | Joined user:{userId} room |
| 6.2 | Connect with no token | Disconnected immediately |
| 6.3 | Send 31 messages in 60s from one socket | 31st emits WsException (rate limited) |
| 6.4 | Disconnect mid-session | Presence updated |
| 6.5 | Typing indicator | Other side receives user:typing |
7. Push integration
| # | Scenario | Expected |
|---|---|---|
| 7.1 | Send DM to a user with newMessage pref true | Push enqueued |
| 7.2 | Same with newMessage opted out | Push skipped |
| 7.3 | Recipient has no device token | Push skipped silently |
8. Permissions
| # | Scenario | Expected |
|---|---|---|
| 8.1 | Cross-org DM attempt | 403 |
| 8.2 | Coach reads another org’s conversations | 403 |
| 8.3 | Member deletes another member’s message | 403 |
9. Smoke
- Two browsers connected → DM is realtime.
- Push arrives on a mobile device.
- Inbox unread badge updates on read.