fix(bruno): convert drifted body keys to body.data — CLI scenario runs now carry bodies

Follow-up to the sweep route audit, correcting its diagnosis: the CLI
DOES read .yml bodies — via OpenCollection's `body.data` string form
(the NewDevDBSetup script always worked because it used it). The
failing scenario POSTs used `body.json` (structured) / `body.jsonBody`
(string) — keys the CLI silently ignores, sending Content-Length: 0
with form-urlencoded content-type, so every server bind failed.

- 81 bodies converted to data-form (json maps serialized; jsonBody
  strings kept verbatim, {{vars}} intact).
- 6 pre-existing TRUNCATED payloads repaired: the original generator
  cut bodies at the first {{var}} (every SHA-256-identified scenario
  plus Sync Progress/Sync Bookmarks shipped mid-JSON and 400'd from
  day one). Rewritten whole, faithful to each file's documented
  purpose; book_sha256/book_uuid added to the environment.
- environments: opds_base_url correction from the sweep retained.

Verified live against the rebuilt dev container: the full koreader
scenario tree runs green through `bru run` — 14/14 (202s across
immediate/checkpoint/SHA-256 progress, 200s bookmarks, highlights,
notes; SHA-256 book identification exercised against a real
file_sha256).
This commit is contained in:
John O'Keefe committed 2026-10-17 12:22:35 -04:00
1 parent 63a20dcc95
commit 09b6693505
83 files changed
+386 -2769

No files matched your search

+4 -104
View File
@@ -8,107 +8,7 @@ http:
auth: inherit
body:
type: json
jsonBody: "{\n \"name\": \"My Sci-Fi Books\",\n \"resource_type\":\
\ \"media-items\",\n \"filters\": {\n \"genre_filter\": \"Science Fiction\"\
,\n \"sort\": \"title ASC\",\n \"author_filter\": \"\"\n }\n\
}"
docs: |-
## Create Saved Filter
Creates a new saved filter for the authenticated user.
**Method:** POST
**Endpoint:** /api/saved-filters
**Authentication:** Required (Bearer token)
**Request Body:**
```json
{
"name": "My Sci-Fi Books",
"resource_type": "media-items",
"filters": {
"genre_filter": "Science Fiction",
"sort": "title ASC",
"author_filter": "",
"series_filter": ""
}
}
```
**Required Fields:**
- `name` (string, required): Filter name
- Must be unique per user + resource type combination
- Maximum 100 characters
- Cannot be empty
- `resource_type` (string, required): Type of resource to filter
- Examples: "media-items", "collections", "devices"
- Must match valid resource types
- `filters` (object, required): Key-value pairs of filter criteria
- Flexible structure - any valid filter fields
- Serialized as JSONB in database
**Common Filter Fields for media-items:**
- `search`: General search term
- `author_filter`: Filter by author name
- `genre_filter`: Filter by genre
- `series_filter`: Filter by series
- `language_filter`: Filter by language
- `year_min`: Minimum copyright year
- `year_max`: Maximum copyright year
- `has_cover`: Boolean for cover image
- `sort`: Sort order (e.g., "title ASC", "created_at DESC")
**Response:**
```json
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "My Sci-Fi Books",
"resource_type": "media-items",
"filters": {
"genre_filter": "Science Fiction",
"sort": "title ASC"
},
"created_at": "2024-03-20T12:00:00Z",
"updated_at": "2024-03-20T12:00:00Z"
}
```
**Status Codes:**
- 201: Created - Filter successfully created
- 400: Bad Request - Invalid request body or missing required fields
- 401: Unauthorized - Invalid or missing authentication token
- 409: Conflict - Filter with this name already exists for this resource type
**Error Response Examples:**
Duplicate name error:
```json
{
"error": "filter with name 'My Sci-Fi Books' already exists for this resource type"
}
```
**Example Usage:**
```bash
curl -X POST \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "My Sci-Fi Books",
"resource_type": "media-items",
"filters": {
"genre_filter": "Science Fiction",
"sort": "title ASC"
}
}' \
"{{base_url}}/api/saved-filters"
```
**Validation Rules:**
- Name must be unique per user + resource type
- Name cannot be empty or whitespace only
- Resource type must be valid
- Filters object must be provided (can be empty object {})
data: "{\n \"name\": \"My Sci-Fi Books\",\n \"resource_type\": \"media-items\",\n \"filters\": {\n \"genre_filter\": \"Science Fiction\",\n \"sort\": \"title ASC\",\n \"author_filter\": \"\"\n }\n}"
docs: "## Create Saved Filter\n\nCreates a new saved filter for the authenticated user.\n\n**Method:** POST\n\n**Endpoint:** /api/saved-filters\n\n**Authentication:** Required (Bearer token)\n\n**Request Body:**\n```json\n{\n \"name\": \"My Sci-Fi Books\",\n \"resource_type\": \"media-items\",\n \"filters\": {\n \"genre_filter\": \"Science Fiction\",\n \"sort\": \"title ASC\",\n \"author_filter\": \"\",\n \"series_filter\": \"\"\n }\n}\n```\n\n**Required Fields:**\n- `name` (string, required): Filter name\n - Must be unique per user + resource type combination\n - Maximum 100 characters\n - Cannot be empty\n- `resource_type` (string, required): Type of resource to filter\n - Examples: \"media-items\", \"collections\", \"devices\"\n - Must match valid resource types\n- `filters` (object, required): Key-value pairs of filter criteria\n - Flexible structure - any valid filter fields\n - Serialized as JSONB in database\n\n**Common Filter Fields for media-items:**\n- `search`:\
\ General search term\n- `author_filter`: Filter by author name\n- `genre_filter`: Filter by genre\n- `series_filter`: Filter by series\n- `language_filter`: Filter by language\n- `year_min`: Minimum copyright year\n- `year_max`: Maximum copyright year\n- `has_cover`: Boolean for cover image\n- `sort`: Sort order (e.g., \"title ASC\", \"created_at DESC\")\n\n**Response:**\n```json\n{\n \"id\": \"550e8400-e29b-41d4-a716-446655440000\",\n \"name\": \"My Sci-Fi Books\",\n \"resource_type\": \"media-items\",\n \"filters\": {\n \"genre_filter\": \"Science Fiction\",\n \"sort\": \"title ASC\"\n },\n \"created_at\": \"2024-03-20T12:00:00Z\",\n \"updated_at\": \"2024-03-20T12:00:00Z\"\n}\n```\n\n**Status Codes:**\n- 201: Created - Filter successfully created\n- 400: Bad Request - Invalid request body or missing required fields\n- 401: Unauthorized - Invalid or missing authentication token\n- 409: Conflict - Filter with this name already exists for this resource type\n\n**Error Response\
\ Examples:**\n\nDuplicate name error:\n```json\n{\n \"error\": \"filter with name 'My Sci-Fi Books' already exists for this resource type\"\n}\n```\n\n**Example Usage:**\n```bash\ncurl -X POST \\\n -H \"Authorization: Bearer YOUR_TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"name\": \"My Sci-Fi Books\",\n \"resource_type\": \"media-items\",\n \"filters\": {\n \"genre_filter\": \"Science Fiction\",\n \"sort\": \"title ASC\"\n }\n }' \\\n \"{{base_url}}/api/saved-filters\"\n```\n\n**Validation Rules:**\n- Name must be unique per user + resource type\n- Name cannot be empty or whitespace only\n- Resource type must be valid\n- Filters object must be provided (can be empty object {})"