# S01: Search Query Logging + Popular Searches API — UAT

**Milestone:** M015
**Written:** 2026-04-03T04:08:38.241Z

## UAT: Search Query Logging + Popular Searches API

### Preconditions
- Chrysopedia deployed on ub01 with containers healthy
- Alembic migration 013 applied (search_log table exists)
- Redis running and accessible

### Test 1: Search logging — normal query
1. Run: `curl -s 'http://ub01:8096/api/v1/search?q=compression&scope=all'`
2. Expected: 200 OK with search results JSON
3. Run: `ssh ub01 "docker exec chrysopedia-db psql -U chrysopedia -t -c \"SELECT query, scope, result_count FROM search_log WHERE query='compression' ORDER BY created_at DESC LIMIT 1;\""`
4. Expected: Row with `compression | all | <some count>`

### Test 2: Empty query not logged
1. Note current count: `ssh ub01 "docker exec chrysopedia-db psql -U chrysopedia -t -c 'SELECT count(*) FROM search_log;'"`
2. Run: `curl -s 'http://ub01:8096/api/v1/search?q=&scope=all'`
3. Run: `curl -s 'http://ub01:8096/api/v1/search?q=%20&scope=all'`
4. Re-check count (after 2s delay)
5. Expected: Count unchanged — empty/whitespace queries are not logged

### Test 3: Popular searches — cache miss (fresh)
1. Clear Redis cache: `ssh ub01 "docker exec chrysopedia-redis redis-cli DEL chrysopedia:popular_searches"`
2. Run: `curl -s 'http://ub01:8096/api/v1/search/popular'`
3. Expected: JSON with `{"items": [...], "cached": false}` — items array (may be empty if no recent searches)

### Test 4: Popular searches — cache hit
1. Run: `curl -s 'http://ub01:8096/api/v1/search/popular'`
2. Expected: JSON with `{"items": [...], "cached": true}` — same items, cached flag true

### Test 5: Popular searches — LOWER normalization
1. Clear cache: `ssh ub01 "docker exec chrysopedia-redis redis-cli DEL chrysopedia:popular_searches"`
2. Search with mixed case: `curl -s 'http://ub01:8096/api/v1/search?q=Reverb&scope=all'`
3. Search lowercase: `curl -s 'http://ub01:8096/api/v1/search?q=reverb&scope=all'`
4. Wait 2s, then: `curl -s 'http://ub01:8096/api/v1/search/popular'`
5. Expected: "reverb" appears once in items with combined count (not two separate entries)

### Test 6: Popular searches — no data (empty table scenario)
1. If possible to test on a clean DB: GET /search/popular should return `{"items": [], "cached": false}`
2. Expected: Empty items array, no 500 error

### Test 7: Redis down fallback
1. Stop Redis: `ssh ub01 "docker compose stop chrysopedia-redis"`
2. Run: `curl -s 'http://ub01:8096/api/v1/search/popular'`
3. Expected: JSON with items from DB (cached: false), no 500 error
4. Restart Redis: `ssh ub01 "docker compose start chrysopedia-redis"`

### Edge Cases
- Scope values: searches with scope=techniques, scope=creators should all log correctly
- Very long query strings: should log without error (VARCHAR default limit)
- Concurrent searches: fire-and-forget pattern should not block or corrupt other requests
