test(bruno): sweep the collection against the actual route table
Audited every request (247 .yml files) against the routes the server
actually registers (extracted from internal/router, group prefixes
resolved). Results:
- 44 stale requests: deleted where the endpoint no longer exists
(universal-progress GET/PUT /api/progress/:id — routes never existed,
the handlers were deleted in H1; queue stats/clear-failed/process;
admin profile page; auth/account) and FIXED where the endpoint exists
under a different path (kobo token-in-path scheme: every
/api/sync/kobo/<seg> and /api/v1/kobo/<seg> URL now carries
{{kobo_device_token}}; /sync/* missing their /api prefix; koreader
/api/v1/koreader/sync/* -> /api/sync/koreader/*; link-book and
unlinked-books moved to /api/devices/:id/sync/*).
- koreader Sync Highlights/Sync Notes pointed at endpoints that never
existed with a body shape to match — rewritten against POST
/api/sync/koreader/bookmarks (the annotations funnel carries
highlights and notes arrays).
- 9 files the GUI refused to load: 4 had bruno-invalid script type
"tests" (converted to after-response), 4 were info/type:collection
doc files with bogus http blocks (converted to folder.yml folder
docs), 1 had a dead pre-YAML schema and no route (deleted).
- devices/koreader requests now carry request-level bearer auth
({{koreader_device_token}}) — they inherited the collection's user-JWT
bearer, which device endpoints rightly reject — and their bodies
reference {{media_item_id}} instead of dead placeholder uuids.
- environments: opds_base_url was empty (every OPDS request missed the
/opds prefix); set to http://localhost:8765/opds and un-doubled the
prefix in opds/scenarios.
Known limitation, for the handoff: bru CLI cannot execute .yml request
bodies (the collection is GUI OpenCollection format; the CLI runner
reads bodies from .bru files only). A .bru migration is the remaining
bruno work if CLI runs are wanted.
This commit is contained in:
1 parent
6808c1babb
commit
a85bc8af9b
65 files changed
+409
-2127
No files matched your search
@@ -8,40 +8,9 @@ http:
|
||||
auth: inherit
|
||||
body:
|
||||
type: json
|
||||
jsonBody: "{\n \"library_id\": \"{{library_id}}\",\n \"name\": \"My Favorites\"\
|
||||
,\n \"icon\": \"⭐\",\n \"description\": \"My favorite books\",\n \"show_on_dashboard\"\
|
||||
: true,\n \"auto_assign_rules\": \"\",\n \"manual_book_ids\": [\"{{media_item_id}}\"\
|
||||
],\n \"match_type\": \"all\"\n}"
|
||||
jsonBody: "{\n \"library_id\": \"{{library_id}}\",\n \"name\": \"My Favorites\",\n \"icon\": \"⭐\",\n \"description\": \"My favorite books\",\n \"show_on_dashboard\": true,\n \"auto_assign_rules\": \"\",\n \"manual_book_ids\": [\"{{media_item_id}}\"],\n \"match_type\": \"all\"\n}"
|
||||
runtime:
|
||||
scripts:
|
||||
- type: tests
|
||||
code: "test(\"status must be 201 with valid request\", function() {\n expect(res.status).to.eql(201);\n\
|
||||
});\n\ntest(\"response contains collection id\", function() {\n expect(res.body.id).to.exist;\n\
|
||||
expect(res.body.id).to.be.a('string');\n });\n\ntest(\"response contains\
|
||||
\ collection name\", function() {\n expect(res.body.name).to.eql(\"My Favorites\"\
|
||||
);\n });"
|
||||
|
||||
docs: |-
|
||||
Create a custom collection with manually selected books.
|
||||
|
||||
**Endpoint**: POST /api/collections
|
||||
**Auth**: Required (Bearer token)
|
||||
|
||||
## Example Request
|
||||
|
||||
```json
|
||||
{
|
||||
"library_id": "cc23c3a7-f8fb-451a-a78d-2a16df1b725a",
|
||||
"name": "My Favorites",
|
||||
"icon": "⭐",
|
||||
"description": "My favorite books",
|
||||
"show_on_dashboard": true,
|
||||
"auto_assign_rules": "",
|
||||
"manual_book_ids": ["book-id-1", "book-id-2"],
|
||||
"match_type": "all"
|
||||
}
|
||||
```
|
||||
|
||||
## Response
|
||||
|
||||
Returns the created collection with generated ID.
|
||||
- type: after-response
|
||||
code: "function onResponse(res) {\ntest(\"status must be 201 with valid request\", function() {\n expect(res.status).to.eql(201);\n});\n\ntest(\"response contains collection id\", function() {\n expect(res.body.id).to.exist;\nexpect(res.body.id).to.be.a('string');\n });\n\ntest(\"response contains collection name\", function() {\n expect(res.body.name).to.eql(\"My Favorites\");\n });\n}"
|
||||
docs: "Create a custom collection with manually selected books.\n\n**Endpoint**: POST /api/collections\n**Auth**: Required (Bearer token)\n\n## Example Request\n\n```json\n{\n \"library_id\": \"cc23c3a7-f8fb-451a-a78d-2a16df1b725a\",\n \"name\": \"My Favorites\",\n \"icon\": \"⭐\",\n \"description\": \"My favorite books\",\n \"show_on_dashboard\": true,\n \"auto_assign_rules\": \"\",\n \"manual_book_ids\": [\"book-id-1\", \"book-id-2\"],\n \"match_type\": \"all\"\n}\n```\n\n## Response\n\nReturns the created collection with generated ID."
|
||||
@@ -8,16 +8,12 @@ http:
|
||||
auth: inherit
|
||||
body:
|
||||
type: json
|
||||
jsonBody: "{\n \"library_id\": \"{{library_id}}\",\n \"icon\": \"📚\"\
|
||||
\n}"
|
||||
jsonBody: "{\n \"library_id\": \"{{library_id}}\",\n \"icon\": \"\U0001F4DA\"\n}"
|
||||
runtime:
|
||||
scripts:
|
||||
- type: tests
|
||||
code: "test(\"status must be 400 with missing name\", function() {\n expect(res.status).to.eql(400);\n\
|
||||
});\n\ntest(\"response contains error message\", function() {\n expect(res.body.error).to.exist;\n\
|
||||
\ });"
|
||||
- type: after-response
|
||||
code: "function onResponse(res) {\ntest(\"status must be 400 with missing name\", function() {\n expect(res.status).to.eql(400);\n});\n\ntest(\"response contains error message\", function() {\n expect(res.body.error).to.exist;\n });\n}"
|
||||
docs: 'Test error handling when creating a collection without required fields.
|
||||
|
||||
docs: |-
|
||||
Test error handling when creating a collection without required fields.
|
||||
|
||||
**Expected Response**: 400 Bad Request with error message.
|
||||
**Expected Response**: 400 Bad Request with error message.'
|
||||
@@ -8,57 +8,10 @@ http:
|
||||
auth: inherit
|
||||
body:
|
||||
type: json
|
||||
jsonBody: "{\n \"library_id\": \"{{library_id}}\",\n \"name\": \"Science Fiction\
|
||||
\ Books\",\n \"icon\": \"🚀\",\n \"description\": \"All my sci-fi books\",\n\
|
||||
\ \"show_on_dashboard\": true,\n \"auto_assign_rules\": \"{\\\"rules\\\":[{\\\
|
||||
\"id\\\":\\\"rule1\\\",\\\"field\\\":\\\"genre\\\",\\\"operator\\\":\\\"equals\\\"\
|
||||
,\\\"value\\\":\\\"Sci-Fi\\\",\\\"priority\\\":1}]}\",\n \"manual_book_ids\"\
|
||||
: [],\n \"match_type\": \"all\"\n}"
|
||||
jsonBody: "{\n \"library_id\": \"{{library_id}}\",\n \"name\": \"Science Fiction Books\",\n \"icon\": \"\U0001F680\",\n \"description\": \"All my sci-fi books\",\n \"show_on_dashboard\": true,\n \"auto_assign_rules\": \"{\\\"rules\\\":[{\\\"id\\\":\\\"rule1\\\",\\\"field\\\":\\\"genre\\\",\\\"operator\\\":\\\"equals\\\",\\\"value\\\":\\\"Sci-Fi\\\",\\\"priority\\\":1}]}\",\n \"manual_book_ids\": [],\n \"match_type\": \"all\"\n}"
|
||||
runtime:
|
||||
scripts:
|
||||
- type: tests
|
||||
code: "test(\"status must be 201 with valid request\", function() {\n expect(res.status).to.eql(201);\n\
|
||||
});\n\ntest(\"response contains collection id\", function() {\n expect(res.body.id).to.exist;\n\
|
||||
expect(res.body.id).to.be.a('string');\n });\n\ntest(\"response contains\
|
||||
\ collection name\", function() {\n expect(res.body.name).to.eql(\"Science\
|
||||
\ Fiction Books\");\n });\n\ntest(\"response contains is_system false\", function()\
|
||||
\ {\n expect(res.body.is_system).to.exist;\n expect(res.body.is_system).to.eql(false);\n\
|
||||
\ });"
|
||||
|
||||
docs: |-
|
||||
Create a custom collection with filter rules.
|
||||
|
||||
**Endpoint**: POST /api/collections
|
||||
**Auth**: Required (Bearer token)
|
||||
|
||||
## Request Body
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| library_id | string | Yes | Library UUID |
|
||||
| name | string | Yes | Collection name |
|
||||
| icon | string | No | Emoji icon |
|
||||
| description | string | No | Collection description |
|
||||
| show_on_dashboard | boolean | No | Show on dashboard (default: true) |
|
||||
| auto_assign_rules | string | No | JSON string of filter rules |
|
||||
| manual_book_ids | array | No | Array of book IDs |
|
||||
| match_type | string | No | "all" (AND) or "any" (OR) |
|
||||
|
||||
## Example Request
|
||||
|
||||
```json
|
||||
{
|
||||
"library_id": "cc23c3a7-f8fb-451a-a78d-2a16df1b725a",
|
||||
"name": "Science Fiction Books",
|
||||
"icon": "🚀",
|
||||
"description": "All my sci-fi books",
|
||||
"show_on_dashboard": true,
|
||||
"auto_assign_rules": "{\"rules\":[{\"id\":\"rule1\",\"field\":\"genre\",\"operator\":\"equals\",\"value\":\"Sci-Fi\",\"priority\":1}]}",
|
||||
"manual_book_ids": [],
|
||||
"match_type": "all"
|
||||
}
|
||||
```
|
||||
|
||||
## Response
|
||||
|
||||
Returns the created collection with generated ID.
|
||||
- type: after-response
|
||||
code: "function onResponse(res) {\ntest(\"status must be 201 with valid request\", function() {\n expect(res.status).to.eql(201);\n});\n\ntest(\"response contains collection id\", function() {\n expect(res.body.id).to.exist;\nexpect(res.body.id).to.be.a('string');\n });\n\ntest(\"response contains collection name\", function() {\n expect(res.body.name).to.eql(\"Science Fiction Books\");\n });\n\ntest(\"response contains is_system false\", function() {\n expect(res.body.is_system).to.exist;\n expect(res.body.is_system).to.eql(false);\n });\n}"
|
||||
docs: "Create a custom collection with filter rules.\n\n**Endpoint**: POST /api/collections\n**Auth**: Required (Bearer token)\n\n## Request Body\n\n| Field | Type | Required | Description |\n|-------|------|----------|-------------|\n| library_id | string | Yes | Library UUID |\n| name | string | Yes | Collection name |\n| icon | string | No | Emoji icon |\n| description | string | No | Collection description |\n| show_on_dashboard | boolean | No | Show on dashboard (default: true) |\n| auto_assign_rules | string | No | JSON string of filter rules |\n| manual_book_ids | array | No | Array of book IDs |\n| match_type | string | No | \"all\" (AND) or \"any\" (OR) |\n\n## Example Request\n\n```json\n{\n \"library_id\": \"cc23c3a7-f8fb-451a-a78d-2a16df1b725a\",\n \"name\": \"Science Fiction Books\",\n \"icon\": \"\U0001F680\",\n \"description\": \"All my sci-fi books\",\n \"show_on_dashboard\": true,\n \"auto_assign_rules\": \"{\\\"rules\\\":[{\\\"id\\\":\\\"rule1\\\",\\\"field\\\":\\\
|
||||
\"genre\\\",\\\"operator\\\":\\\"equals\\\",\\\"value\\\":\\\"Sci-Fi\\\",\\\"priority\\\":1}]}\",\n \"manual_book_ids\": [],\n \"match_type\": \"all\"\n}\n```\n\n## Response\n\nReturns the created collection with generated ID."
|
||||
@@ -8,53 +8,9 @@ http:
|
||||
auth: inherit
|
||||
body:
|
||||
type: json
|
||||
jsonBody: "{\n \"library_id\": \"{{library_id}}\",\n \"rules\": [\n\
|
||||
{\n \"id\": \"rule1\",\n \"field\": \"genre\",\n\
|
||||
\"operator\": \"equals\",\n \"value\": \"Fiction\",\n\
|
||||
\"priority\": 1\n }\n ],\n \"manual_book_ids\": [],\n \"limit\": 20\n}"
|
||||
jsonBody: "{\n \"library_id\": \"{{library_id}}\",\n \"rules\": [\n{\n \"id\": \"rule1\",\n \"field\": \"genre\",\n\"operator\": \"equals\",\n \"value\": \"Fiction\",\n\"priority\": 1\n }\n ],\n \"manual_book_ids\": [],\n \"limit\": 20\n}"
|
||||
runtime:
|
||||
scripts:
|
||||
- type: tests
|
||||
code: "test(\"status must be 200 with valid request\", function() {\n expect(res.status).to.eql(200);\n\
|
||||
});\n\ntest(\"response contains items array\", function() {\n expect(res.body.items).to.exist;\n\
|
||||
expect(res.body.items).to.be.an('array');\n });"
|
||||
|
||||
docs: |-
|
||||
Preview collection with filter rules before saving.
|
||||
|
||||
**Endpoint**: POST /api/collections/preview
|
||||
**Auth**: Required (Bearer token)
|
||||
|
||||
## Request Body
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| library_id | string | Yes | Library UUID |
|
||||
| rules | array | No | Filter rules to evaluate |
|
||||
| manual_book_ids | array | No | Manually selected book IDs |
|
||||
| limit | int | No | Max items to return (default: 20, max: 100) |
|
||||
|
||||
## Example Request
|
||||
|
||||
```json
|
||||
{
|
||||
"library_id": "cc23c3a7-f8fb-451a-a78d-2a16df1b725a",
|
||||
"rules": [
|
||||
{
|
||||
"id": "rule1",
|
||||
"field": "genre",
|
||||
"operator": "equals",
|
||||
"value": "Fiction",
|
||||
"priority": 1
|
||||
}
|
||||
],
|
||||
"manual_book_ids": [],
|
||||
"limit": 20
|
||||
}
|
||||
```
|
||||
|
||||
## Response
|
||||
|
||||
Returns array of matching book items.
|
||||
|
||||
This endpoint is used by the Custom Section Builder to show users what books will match their filter rules before they save the collection.
|
||||
- type: after-response
|
||||
code: "function onResponse(res) {\ntest(\"status must be 200 with valid request\", function() {\n expect(res.status).to.eql(200);\n});\n\ntest(\"response contains items array\", function() {\n expect(res.body.items).to.exist;\nexpect(res.body.items).to.be.an('array');\n });\n}"
|
||||
docs: "Preview collection with filter rules before saving.\n\n**Endpoint**: POST /api/collections/preview\n**Auth**: Required (Bearer token)\n\n## Request Body\n\n| Field | Type | Required | Description |\n|-------|------|----------|-------------|\n| library_id | string | Yes | Library UUID |\n| rules | array | No | Filter rules to evaluate |\n| manual_book_ids | array | No | Manually selected book IDs |\n| limit | int | No | Max items to return (default: 20, max: 100) |\n\n## Example Request\n\n```json\n{\n \"library_id\": \"cc23c3a7-f8fb-451a-a78d-2a16df1b725a\",\n \"rules\": [\n {\n \"id\": \"rule1\",\n \"field\": \"genre\",\n \"operator\": \"equals\",\n \"value\": \"Fiction\",\n \"priority\": 1\n }\n ],\n \"manual_book_ids\": [],\n \"limit\": 20\n}\n```\n\n## Response\n\nReturns array of matching book items.\n\nThis endpoint is used by the Custom Section Builder to show users what books will match their filter rules before they save the collection."
|
||||
Reference in new issue
Block a user