# S03: [A] Consent Data Model + API Endpoints — UAT

**Milestone:** M019
**Written:** 2026-04-03T22:18:33.593Z

# S03 UAT: Consent Data Model + API Endpoints

## Preconditions
- PostgreSQL running on ub01:5433 with chrysopedia database
- Alembic migration 017 applied (`docker exec chrysopedia-api alembic upgrade head`)
- API running at ub01:8096
- A registered creator user with at least 2 source videos
- A registered admin user (role=admin)

## Test Cases

### TC1: Unauthenticated Access Blocked
1. `curl -s -o /dev/null -w '%{http_code}' http://ub01:8096/api/v1/consent/videos` → **401**
2. `curl -s -o /dev/null -w '%{http_code}' -X PUT http://ub01:8096/api/v1/consent/videos/{any-uuid}` → **401**

### TC2: User Without Creator ID Gets 403
1. Register a new user (no creator linkage)
2. `GET /api/v1/consent/videos` with auth headers → **403** with message about creator_id

### TC3: Creator Lists Own Videos
1. Authenticate as creator user
2. `GET /api/v1/consent/videos` → **200** with `items` array containing only this creator's videos
3. Each item has `kb_inclusion`, `training_usage`, `public_display` fields with defaults (false, false, true)
4. Each item includes `video_filename`

### TC4: Creator Cannot Access Other Creator's Video
1. Authenticate as creator A
2. `GET /api/v1/consent/videos/{creator_b_video_id}` → **403**
3. `PUT /api/v1/consent/videos/{creator_b_video_id}` → **403**

### TC5: First PUT Creates Consent Record + Audit Entries
1. `PUT /api/v1/consent/videos/{video_id}` with `{"kb_inclusion": true, "training_usage": true}`
2. Response **200** with `kb_inclusion=true`, `training_usage=true`, `public_display=true` (default)
3. `GET /api/v1/consent/videos/{video_id}/history` → 2 audit entries (one per changed field), version 1 and 2

### TC6: Idempotent PUT Creates No New Audit Entries
1. After TC5, repeat the same PUT: `{"kb_inclusion": true, "training_usage": true}`
2. Response **200** with same values
3. `GET /api/v1/consent/videos/{video_id}/history` → still 2 entries (no new ones)

### TC7: Partial Update Changes Only Specified Field
1. `PUT /api/v1/consent/videos/{video_id}` with `{"public_display": false}`
2. Response shows `kb_inclusion=true` (unchanged), `training_usage=true` (unchanged), `public_display=false` (changed)
3. History shows 1 new audit entry for `public_display` only

### TC8: Audit Trail Ordered by Version
1. `GET /api/v1/consent/videos/{video_id}/history`
2. Entries ordered by ascending version number
3. Each entry has: `version`, `field_name`, `old_value`, `new_value`, `changed_by`, `created_at`

### TC9: Pagination on List Endpoint
1. Creator has 2+ videos
2. `GET /api/v1/consent/videos?limit=1&offset=0` → 1 item, `total` ≥ 2
3. `GET /api/v1/consent/videos?limit=1&offset=1` → 1 different item

### TC10: Nonexistent Video Returns 404
1. `GET /api/v1/consent/videos/{random-uuid}` → **404**
2. `PUT /api/v1/consent/videos/{random-uuid}` → **404**
3. `GET /api/v1/consent/videos/{random-uuid}/history` → **404**

### TC11: Admin Can Read Any Creator's Consent
1. Authenticate as admin
2. `GET /api/v1/consent/videos/{any_creator_video_id}` → **200** (not 403)

### TC12: Admin Summary Endpoint
1. Authenticate as admin
2. `GET /api/v1/consent/admin/summary` → **200** with `total_videos`, `kb_inclusion_granted`, `training_usage_granted`, `public_display_granted` counts
3. Non-admin user: `GET /api/v1/consent/admin/summary` → **403**

## Edge Cases
- Empty PUT body `{}` → 200, no fields changed, no audit entries created
- Non-UUID in video_id path parameter → 422 validation error
