Body Metrics — QA Plan
1. Create
| # | Scenario | Expected |
|---|---|---|
| 1.1 | Member self-reports weight 78.5 kg today | 201; row inserted with recorded_by=user.id |
| 1.2 | Coach records body fat on member | 201; same row; recorded_by=coach.user.id |
| 1.3 | Same member, same date, same metric twice | Second returns conflict error from unique index |
| 1.4 | Custom metric with label “forearm” | 201; subsequent forearm on same day collides |
| 1.5 | Invalid unit for the type (e.g. kg for body_fat) | DTO rejects (or service-layer) — 400 |
| 1.6 | Future recorded_at | Allowed today; flag for product if undesired |
2. List + summary
| # | Scenario | Expected |
|---|---|---|
| 2.1 | List with type=weight and from/to | Returns rows in range, newest first |
| 2.2 | Summary returns latest + previous per (type, label) bucket | Verify delta value |
| 2.3 | Soft-deleted rows excluded | Confirm with deleted row in fixture |
3. Update / delete
| # | Scenario | Expected |
|---|---|---|
| 3.1 | Member updates own row | 200 |
| 3.2 | Member updates another member’s row | 403 |
| 3.3 | Soft delete | deleted_at set; subsequent list excludes |
4. Settings
| # | Scenario | Expected |
|---|---|---|
| 4.1 | First-time settings PUT creates a row | 200 |
| 4.2 | Second PUT updates the same row | 200; no duplicate |
| 4.3 | Disable body_fat for a member | UI hides the metric in self-report; data still readable |
5. Permissions
| # | Scenario | Expected |
|---|---|---|
| 5.1 | Member of org A queries org B membership | 403 |
| 5.2 | Coach with member role tries staff routes | 403 |
| 5.3 | Owner can do everything | 200 |
6. Smoke
- Self-report on mobile → entry appears in coach’s member view.
- Trend chart renders with at least 2 data points (verify after seeding).
- Custom-label dedup works.