refactor(bruno): migrate API tests to Bruno DSL format
- Convert all existing .bru files from JSON to Bruno DSL format - Remove obsolete files (conflicts/api.bru, kobo/Kobo Initialization.bru) - Update auth configuration to use 'inherit' instead of explicit bearer tokens - Add comprehensive documentation to all test files - Improve test scripts with proper assertions and error handling
This commit is contained in:
@@ -7,12 +7,9 @@ meta {
|
||||
post {
|
||||
url: {{base_url}}/api/collections/{{collection_id}}/books
|
||||
body: json
|
||||
auth: bearer
|
||||
auth: inherit
|
||||
}
|
||||
|
||||
auth:bearer {
|
||||
token: {{token}}
|
||||
}
|
||||
|
||||
body:json {
|
||||
{
|
||||
|
||||
@@ -1,49 +1,103 @@
|
||||
{
|
||||
"meta": {
|
||||
"name": "Bulk Add Books to Collections",
|
||||
"type": "http",
|
||||
"event": [
|
||||
meta {
|
||||
name: Bulk Add Books to Collections
|
||||
type: http
|
||||
seq: 1
|
||||
}
|
||||
|
||||
post {
|
||||
url: {{baseUrl}}/api/collections/bulk-add-books
|
||||
body: json
|
||||
auth: inherit
|
||||
}
|
||||
|
||||
headers {
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer {{authToken}}
|
||||
}
|
||||
|
||||
body:json {
|
||||
{
|
||||
"operations": [
|
||||
{
|
||||
"listen": "test",
|
||||
"script": {
|
||||
"exec": [
|
||||
"// Test bulk add to collections response",
|
||||
"if (response.status === 200) {",
|
||||
" tests['Bulk add successful'] = true;",
|
||||
" const body = JSON.parse(response.body);",
|
||||
" tests['Has results array'] = Array.isArray(body.results);",
|
||||
" tests('All operations processed', body.results.length > 0);",
|
||||
"} else {",
|
||||
" tests['Bulk add failed'] = false;",
|
||||
"}"
|
||||
]
|
||||
}
|
||||
"collection_id": "{{collectionId1}}",
|
||||
"book_ids": [
|
||||
"{{bookId1}}",
|
||||
"{{bookId2}}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"collection_id": "{{collectionId2}}",
|
||||
"book_ids": [
|
||||
"{{bookId3}}"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"req": {
|
||||
"url": "{{baseUrl}}/api/collections/bulk-add-books",
|
||||
"method": "POST",
|
||||
"headers": {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": "Bearer {{authToken}}"
|
||||
},
|
||||
"body": {
|
||||
"operations": [
|
||||
{
|
||||
"collection_id": "{{collectionId1}}",
|
||||
"book_ids": [
|
||||
"{{bookId1}}",
|
||||
"{{bookId2}}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"collection_id": "{{collectionId2}}",
|
||||
"book_ids": [
|
||||
"{{bookId3}}"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
script:post-response {
|
||||
function onResponse(res) {
|
||||
if (res.getStatus() === 200) {
|
||||
tests['Bulk add successful'] = true;
|
||||
const body = res.getBody();
|
||||
tests['Has results array'] = Array.isArray(body.results);
|
||||
tests['All operations processed'] = body.results.length > 0;
|
||||
} else {
|
||||
tests['Bulk add failed'] = false;
|
||||
}
|
||||
}
|
||||
onResponse(res);
|
||||
}
|
||||
|
||||
settings {
|
||||
encodeUrl: true
|
||||
timeout: 0
|
||||
}
|
||||
|
||||
docs {
|
||||
## Bulk Add Books to Collections
|
||||
|
||||
Adds multiple books to multiple collections in a single request. Each operation specifies a collection and a list of books to add.
|
||||
|
||||
**Method:** POST
|
||||
|
||||
**Endpoint:** /api/collections/bulk-add-books
|
||||
|
||||
**Authentication:** Bearer token
|
||||
|
||||
**Request Body:**
|
||||
- `operations` (array): Array of collection-book operations
|
||||
- `collection_id` (string): Collection UUID
|
||||
- `book_ids` (array): Array of book UUIDs to add to the collection
|
||||
|
||||
**Response:**
|
||||
- `results` (array): Results for each operation
|
||||
- `total` (number): Total number of operations
|
||||
- `success` (number): Number of successful operations
|
||||
- `failed` (number): Number of failed operations
|
||||
|
||||
**Status Codes:**
|
||||
- 200: Success (with partial results if some failed)
|
||||
- 400: Invalid request data
|
||||
- 401: Unauthorized
|
||||
- 403: Forbidden
|
||||
- 500: Internal server error
|
||||
|
||||
**Example:**
|
||||
```json
|
||||
{
|
||||
"operations": [
|
||||
{
|
||||
"collection_id": "collection-uuid-1",
|
||||
"book_ids": ["book-1", "book-2"]
|
||||
},
|
||||
{
|
||||
"collection_id": "collection-uuid-2",
|
||||
"book_ids": ["book-3"]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Note:** Adding a book that's already in a collection is idempotent (no error).
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
meta {
|
||||
name: Bulk Remove Books - All Books
|
||||
type: http
|
||||
seq: 5
|
||||
}
|
||||
|
||||
post {
|
||||
url: {{baseUrl}}/api/collections/{{collection_id}}/books/bulk-remove
|
||||
body: json
|
||||
auth: inherit
|
||||
}
|
||||
|
||||
headers {
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer {{authToken}}
|
||||
}
|
||||
|
||||
body:json {
|
||||
{
|
||||
"book_ids": [
|
||||
"{{bookId1}}",
|
||||
"{{bookId2}}",
|
||||
"{{bookId3}}"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
script:post-response {
|
||||
function onResponse(res) {
|
||||
if (res.getStatus() === 200) {
|
||||
tests['All books removed'] = true;
|
||||
const body = res.getBody();
|
||||
tests['Removed equals total'] = body.removed === body.total;
|
||||
tests['Total is 3'] = body.total === 3;
|
||||
}
|
||||
}
|
||||
onResponse(res);
|
||||
}
|
||||
|
||||
settings {
|
||||
encodeUrl: true
|
||||
timeout: 0
|
||||
}
|
||||
|
||||
docs {
|
||||
## Bulk Remove Books - All Books
|
||||
|
||||
Tests removing multiple books from a collection in a single request.
|
||||
|
||||
**Expected Result:** 200 OK with removed: 3, total: 3
|
||||
|
||||
**Purpose:** Verifies the bulk remove functionality works correctly when removing multiple books that all exist in the collection.
|
||||
|
||||
**Use Case:** Common scenario when a user wants to remove several books from a collection at once, such as when reorganizing their library or removing books they've finished reading.
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
meta {
|
||||
name: Bulk Remove Books - Empty List
|
||||
type: http
|
||||
seq: 2
|
||||
}
|
||||
|
||||
post {
|
||||
url: {{baseUrl}}/api/collections/{{collection_id}}/books/bulk-remove
|
||||
body: json
|
||||
auth: inherit
|
||||
}
|
||||
|
||||
headers {
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer {{authToken}}
|
||||
}
|
||||
|
||||
body:json {
|
||||
{
|
||||
"book_ids": []
|
||||
}
|
||||
}
|
||||
|
||||
script:post-response {
|
||||
function onResponse(res) {
|
||||
tests['Returns 400 for empty list'] = res.getStatus() === 400;
|
||||
}
|
||||
onResponse(res);
|
||||
}
|
||||
|
||||
settings {
|
||||
encodeUrl: true
|
||||
timeout: 0
|
||||
}
|
||||
|
||||
docs {
|
||||
## Bulk Remove Books - Empty List Validation
|
||||
|
||||
Tests validation behavior when providing an empty book_ids array.
|
||||
|
||||
**Expected Result:** 400 Bad Request
|
||||
|
||||
**Validation Rule:** book_ids array must contain at least one book UUID.
|
||||
|
||||
**Purpose:** Ensures the API properly validates input and rejects empty removal requests.
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
meta {
|
||||
name: Bulk Remove Books - Invalid IDs
|
||||
type: http
|
||||
seq: 4
|
||||
}
|
||||
|
||||
post {
|
||||
url: {{baseUrl}}/api/collections/{{collection_id}}/books/bulk-remove
|
||||
body: json
|
||||
auth: inherit
|
||||
}
|
||||
|
||||
headers {
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer {{authToken}}
|
||||
}
|
||||
|
||||
body:json {
|
||||
{
|
||||
"book_ids": [
|
||||
"{{bookId1}}",
|
||||
"invalid-uuid-format",
|
||||
"{{bookId2}}"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
script:post-response {
|
||||
function onResponse(res) {
|
||||
if (res.getStatus() === 200) {
|
||||
tests['Partial success accepted'] = true;
|
||||
const body = res.getBody();
|
||||
tests['Has removed count'] = body.removed !== undefined;
|
||||
tests['Has results array'] = Array.isArray(body.results);
|
||||
}
|
||||
}
|
||||
onResponse(res);
|
||||
}
|
||||
|
||||
settings {
|
||||
encodeUrl: true
|
||||
timeout: 0
|
||||
}
|
||||
|
||||
docs {
|
||||
## Bulk Remove Books - Invalid IDs
|
||||
|
||||
Tests behavior when the book_ids array contains invalid UUID formats or non-existent books.
|
||||
|
||||
**Expected Result:** 200 OK with partial success
|
||||
|
||||
**Purpose:** Verifies that:
|
||||
- Invalid UUID formats don't crash the endpoint
|
||||
- Non-existent book IDs are handled gracefully
|
||||
- Valid IDs in the same request are still processed
|
||||
- Response includes detailed results showing which succeeded/failed
|
||||
|
||||
**Note:** The endpoint should process all valid IDs and report failures for invalid ones, allowing clients to handle partial failures appropriately.
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
meta {
|
||||
name: Bulk Remove Books - Single Book
|
||||
type: http
|
||||
seq: 3
|
||||
}
|
||||
|
||||
post {
|
||||
url: {{baseUrl}}/api/collections/{{collection_id}}/books/bulk-remove
|
||||
body: json
|
||||
auth: inherit
|
||||
}
|
||||
|
||||
headers {
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer {{authToken}}
|
||||
}
|
||||
|
||||
body:json {
|
||||
{
|
||||
"book_ids": [
|
||||
"{{bookId1}}"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
script:post-response {
|
||||
function onResponse(res) {
|
||||
if (res.getStatus() === 200) {
|
||||
const body = res.getBody();
|
||||
tests['Single book removed'] = body.removed === 1;
|
||||
tests['Total is 1'] = body.total === 1;
|
||||
}
|
||||
}
|
||||
onResponse(res);
|
||||
}
|
||||
|
||||
settings {
|
||||
encodeUrl: true
|
||||
timeout: 0
|
||||
}
|
||||
|
||||
docs {
|
||||
## Bulk Remove Books - Single Book
|
||||
|
||||
Tests that bulk remove endpoint works correctly with a single book.
|
||||
|
||||
**Expected Result:** 200 OK with removed: 1, total: 1
|
||||
|
||||
**Purpose:** Verifies the bulk remove endpoint handles single-item arrays correctly, providing flexibility for clients to use the same endpoint for both single and multiple removals.
|
||||
|
||||
**Note:** Using bulk remove for a single book is functionally equivalent to the single remove endpoint but allows for consistent error handling and response format.
|
||||
}
|
||||
@@ -1,103 +1,116 @@
|
||||
meta {
|
||||
name: "Bulk Remove Books from Collection"
|
||||
name: Bulk Remove Books from Collection
|
||||
type: http
|
||||
seq: 1
|
||||
}
|
||||
|
||||
# Setup: Create a test collection first
|
||||
post {
|
||||
url: {{baseUrl}}/api/collections
|
||||
body: {
|
||||
name: "Bulk Remove Test Collection"
|
||||
description: "Collection for testing bulk remove"
|
||||
color: "#FF5733"
|
||||
icon: "📚"
|
||||
}
|
||||
assert {
|
||||
res.status: 200
|
||||
}
|
||||
# Store collection_id from response
|
||||
# Note: In actual Bruno, you'd use variables
|
||||
url: {{baseUrl}}/api/collections/{{collection_id}}/books/bulk-remove
|
||||
body: json
|
||||
auth: inherit
|
||||
}
|
||||
|
||||
# Add some books to the collection
|
||||
post {
|
||||
url: {{baseUrl}}/api/collections/{collection_id}/books
|
||||
body: {
|
||||
book_ids: [
|
||||
"book-id-1",
|
||||
"book-id-2",
|
||||
"book-id-3"
|
||||
headers {
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer {{authToken}}
|
||||
}
|
||||
|
||||
body:json {
|
||||
{
|
||||
"book_ids": [
|
||||
"{{bookId1}}",
|
||||
"{{bookId2}}",
|
||||
"{{bookId3}}"
|
||||
]
|
||||
}
|
||||
assert {
|
||||
res.status: 204
|
||||
}
|
||||
}
|
||||
|
||||
# Test 1: Bulk remove all books
|
||||
post {
|
||||
url: {{baseUrl}}/api/collections/{collection_id}/books/bulk-remove
|
||||
body: {
|
||||
book_ids: [
|
||||
"book-id-1",
|
||||
"book-id-2",
|
||||
"book-id-3"
|
||||
script:post-response {
|
||||
function onResponse(res) {
|
||||
if (res.getStatus() === 200) {
|
||||
tests['Bulk remove successful'] = true;
|
||||
const body = res.getBody();
|
||||
tests['Has removed count'] = body.removed !== undefined;
|
||||
tests['Has total count'] = body.total !== undefined;
|
||||
tests['Has results array'] = Array.isArray(body.results);
|
||||
} else {
|
||||
tests['Bulk remove failed'] = false;
|
||||
}
|
||||
}
|
||||
onResponse(res);
|
||||
}
|
||||
|
||||
settings {
|
||||
encodeUrl: true
|
||||
timeout: 0
|
||||
}
|
||||
|
||||
docs {
|
||||
## Bulk Remove Books from Collection
|
||||
|
||||
Removes multiple books from a collection in a single request.
|
||||
|
||||
**Method:** POST
|
||||
|
||||
**Endpoint:** /api/collections/{collection_id}/books/bulk-remove
|
||||
|
||||
**Authentication:** Bearer token
|
||||
|
||||
**Path Parameters:**
|
||||
- `collection_id` (string): Collection UUID
|
||||
|
||||
**Request Body:**
|
||||
- `book_ids` (array): Array of book UUIDs to remove from the collection
|
||||
|
||||
**Response:**
|
||||
- `removed` (number): Number of books successfully removed
|
||||
- `total` (number): Total number of books processed
|
||||
- `results` (array): Results for each removal attempt
|
||||
- `book_id` (string): Book UUID
|
||||
- `success` (boolean): Whether the removal succeeded
|
||||
- `error` (string, optional): Error message if failed
|
||||
|
||||
**Status Codes:**
|
||||
- 200: Success (with partial results if some failed)
|
||||
- 400: Invalid request data (e.g., empty book_ids array)
|
||||
- 401: Unauthorized
|
||||
- 403: Forbidden
|
||||
- 404: Collection not found
|
||||
- 500: Internal server error
|
||||
|
||||
**Example Request:**
|
||||
```json
|
||||
{
|
||||
"book_ids": [
|
||||
"book-uuid-1",
|
||||
"book-uuid-2",
|
||||
"book-uuid-3"
|
||||
]
|
||||
}
|
||||
assert {
|
||||
res.status: 200
|
||||
res.body.removed: #number
|
||||
res.body.total: 3
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# Test 2: Bulk remove with some invalid IDs
|
||||
post {
|
||||
url: {{baseUrl}}/api/collections/{collection_id}/books/bulk-remove
|
||||
body: {
|
||||
book_ids: [
|
||||
"book-id-4",
|
||||
"invalid-id",
|
||||
"book-id-5"
|
||||
**Example Response:**
|
||||
```json
|
||||
{
|
||||
"removed": 2,
|
||||
"total": 3,
|
||||
"results": [
|
||||
{
|
||||
"book_id": "book-uuid-1",
|
||||
"success": true
|
||||
},
|
||||
{
|
||||
"book_id": "book-uuid-2",
|
||||
"success": true
|
||||
},
|
||||
{
|
||||
"book_id": "book-uuid-3",
|
||||
"success": false,
|
||||
"error": "Book not in collection"
|
||||
}
|
||||
]
|
||||
}
|
||||
assert {
|
||||
res.status: 200
|
||||
res.body.removed: #number
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# Test 3: Empty list (should fail validation)
|
||||
post {
|
||||
url: {{baseUrl}}/api/collections/{collection_id}/books/bulk-remove
|
||||
body: {
|
||||
book_ids: []
|
||||
}
|
||||
assert {
|
||||
res.status: 400
|
||||
}
|
||||
}
|
||||
|
||||
# Test 4: Single book (bulk remove should work for 1 book too)
|
||||
post {
|
||||
url: {{baseUrl}}/api/collections/{collection_id}/books/bulk-remove
|
||||
body: {
|
||||
book_ids: [
|
||||
"book-id-6"
|
||||
]
|
||||
}
|
||||
assert {
|
||||
res.status: 200
|
||||
res.body.removed: 1
|
||||
res.body.total: 1
|
||||
}
|
||||
}
|
||||
|
||||
# Cleanup: Delete test collection
|
||||
delete {
|
||||
url: {{baseUrl}}/api/collections/{collection_id}
|
||||
assert {
|
||||
res.status: 204
|
||||
}
|
||||
**Note:** Removing a book that's not in the collection returns success: false for that book but doesn't fail the entire request. Empty book_ids array returns 400.
|
||||
}
|
||||
|
||||
@@ -7,12 +7,9 @@ meta {
|
||||
post {
|
||||
url: {{base_url}}/api/collections
|
||||
body: json
|
||||
auth: bearer
|
||||
auth: inherit
|
||||
}
|
||||
|
||||
auth:bearer {
|
||||
token: {{token}}
|
||||
}
|
||||
|
||||
body:json {
|
||||
{
|
||||
|
||||
@@ -7,10 +7,9 @@ meta {
|
||||
post {
|
||||
url: {{base_url}}/api/devices/{{device_id}}/collections
|
||||
body: json
|
||||
auth: bearer
|
||||
auth: inherit
|
||||
}
|
||||
|
||||
auth:bearer {
|
||||
token: {{token}}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,9 +6,5 @@ meta {
|
||||
|
||||
delete {
|
||||
url: {{base_url}}/api/collections/{{collection_id}}
|
||||
auth: bearer
|
||||
}
|
||||
|
||||
auth:bearer {
|
||||
token: {{token}}
|
||||
auth: inherit
|
||||
}
|
||||
|
||||
@@ -6,9 +6,5 @@ meta {
|
||||
|
||||
delete {
|
||||
url: {{base_url}}/api/devices/{{device_id}}/collections/{{mapping_id}}
|
||||
auth: bearer
|
||||
}
|
||||
|
||||
auth:bearer {
|
||||
token: {{token}}
|
||||
auth: inherit
|
||||
}
|
||||
|
||||
@@ -6,9 +6,5 @@ meta {
|
||||
|
||||
get {
|
||||
url: {{base_url}}/api/collections/books/{{book_id}}
|
||||
auth: bearer
|
||||
}
|
||||
|
||||
auth:bearer {
|
||||
token: {{token}}
|
||||
auth: inherit
|
||||
}
|
||||
|
||||
@@ -6,9 +6,5 @@ meta {
|
||||
|
||||
get {
|
||||
url: {{base_url}}/api/collections/{{collection_id}}
|
||||
auth: bearer
|
||||
}
|
||||
|
||||
auth:bearer {
|
||||
token: {{token}}
|
||||
auth: inherit
|
||||
}
|
||||
|
||||
@@ -6,9 +6,5 @@ meta {
|
||||
|
||||
get {
|
||||
url: {{base_url}}/api/collections?include_auto=true&sort_by=name
|
||||
auth: bearer
|
||||
}
|
||||
|
||||
auth:bearer {
|
||||
token: {{token}}
|
||||
auth: inherit
|
||||
}
|
||||
|
||||
@@ -6,9 +6,5 @@ meta {
|
||||
|
||||
get {
|
||||
url: {{base_url}}/api/devices/{{device_id}}/collections
|
||||
auth: bearer
|
||||
}
|
||||
|
||||
auth:bearer {
|
||||
token: {{token}}
|
||||
auth: inherit
|
||||
}
|
||||
|
||||
@@ -6,9 +6,5 @@ meta {
|
||||
|
||||
delete {
|
||||
url: {{base_url}}/api/collections/{{collection_id}}/books/{{book_id}}
|
||||
auth: bearer
|
||||
}
|
||||
|
||||
auth:bearer {
|
||||
token: {{token}}
|
||||
auth: inherit
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
meta {
|
||||
name: Test Collection Rules - Author Contains
|
||||
type: http
|
||||
seq: 2
|
||||
}
|
||||
|
||||
post {
|
||||
url: {{baseUrl}}/api/collections/test-rules
|
||||
body: json
|
||||
auth: inherit
|
||||
}
|
||||
|
||||
headers {
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer {{authToken}}
|
||||
}
|
||||
|
||||
body:json {
|
||||
{
|
||||
"rules": [
|
||||
{
|
||||
"field": "author",
|
||||
"operator": "contains",
|
||||
"value": "Asimov"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
script:post-response {
|
||||
function onResponse(res) {
|
||||
if (res.getStatus() === 200) {
|
||||
tests['Author search successful'] = true;
|
||||
const body = res.getBody();
|
||||
tests['Has matches array'] = Array.isArray(body.matches);
|
||||
tests['Found books by Asimov'] = body.matches.length > 0;
|
||||
} else {
|
||||
tests['Author search failed'] = false;
|
||||
}
|
||||
}
|
||||
onResponse(res);
|
||||
}
|
||||
|
||||
settings {
|
||||
encodeUrl: true
|
||||
timeout: 0
|
||||
}
|
||||
|
||||
docs {
|
||||
## Test Collection Rules - Author Contains
|
||||
|
||||
Tests the "contains" operator on the author field to find books by a specific author (partial match).
|
||||
|
||||
**Example Use Case:** Finding all books by an author whose name contains "Asimov" (e.g., "Isaac Asimov").
|
||||
|
||||
**Operator:** `contains` - Matches if the field contains the specified value as a substring (case-insensitive typically).
|
||||
|
||||
**Expected Result:** Returns all books where the author field contains "Asimov".
|
||||
|
||||
**Purpose:** Demonstrates text-based partial matching for author searches, useful when you don't need the exact author name or want to find books by authors with similar names.
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
meta {
|
||||
name: Test Collection Rules - Copyright Year Greater Than
|
||||
type: http
|
||||
seq: 3
|
||||
}
|
||||
|
||||
post {
|
||||
url: {{baseUrl}}/api/collections/test-rules
|
||||
body: json
|
||||
auth: inherit
|
||||
}
|
||||
|
||||
headers {
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer {{authToken}}
|
||||
}
|
||||
|
||||
body:json {
|
||||
{
|
||||
"rules": [
|
||||
{
|
||||
"field": "copyright_year",
|
||||
"operator": "greater_than",
|
||||
"value": "2000"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
script:post-response {
|
||||
function onResponse(res) {
|
||||
if (res.getStatus() === 200) {
|
||||
tests['Year comparison successful'] = true;
|
||||
const body = res.getBody();
|
||||
tests['Has matches array'] = Array.isArray(body.matches);
|
||||
tests('Found books after 2000', body.matches.length >= 0);
|
||||
} else {
|
||||
tests['Year comparison failed'] = false;
|
||||
}
|
||||
}
|
||||
onResponse(res);
|
||||
}
|
||||
|
||||
settings {
|
||||
encodeUrl: true
|
||||
timeout: 0
|
||||
}
|
||||
|
||||
docs {
|
||||
## Test Collection Rules - Copyright Year Greater Than
|
||||
|
||||
Tests the "greater_than" operator on the copyright_year field to find books published after a specific year.
|
||||
|
||||
**Example Use Case:** Creating a "Modern Books" collection with books published after 2000.
|
||||
|
||||
**Operator:** `greater_than` - Matches if the field value is greater than the specified value (numeric comparison).
|
||||
|
||||
**Field:** `copyright_year` - The year the book was copyrighted/published.
|
||||
|
||||
**Expected Result:** Returns all books with copyright_year greater than 2000 (i.e., published in 2001 or later).
|
||||
|
||||
**Purpose:** Demonstrates numeric comparison operators for creating date-based collections, useful for organizing books by publication era.
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
meta {
|
||||
name: Test Collection Rules - Empty Rules Array
|
||||
type: http
|
||||
seq: 5
|
||||
}
|
||||
|
||||
post {
|
||||
url: {{baseUrl}}/api/collections/test-rules
|
||||
body: json
|
||||
auth: inherit
|
||||
}
|
||||
|
||||
headers {
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer {{authToken}}
|
||||
}
|
||||
|
||||
body:json {
|
||||
{
|
||||
"rules": []
|
||||
}
|
||||
}
|
||||
|
||||
script:post-response {
|
||||
function onResponse(res) {
|
||||
tests['Empty rules rejected'] = res.getStatus() === 400;
|
||||
}
|
||||
onResponse(res);
|
||||
}
|
||||
|
||||
settings {
|
||||
encodeUrl: true
|
||||
timeout: 0
|
||||
}
|
||||
|
||||
docs {
|
||||
## Test Collection Rules - Empty Rules Array
|
||||
|
||||
Tests validation behavior when providing an empty rules array.
|
||||
|
||||
**Expected Result:** 400 Bad Request
|
||||
|
||||
**Validation Rule:** rules array must contain at least one rule object.
|
||||
|
||||
**Purpose:** Ensures the API properly validates input and rejects empty rule sets, preventing accidental queries that would return all books or cause performance issues.
|
||||
|
||||
**Use Case:** Client-side validation should prevent sending empty rules, but the API should also validate to catch malformed requests.
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
meta {
|
||||
name: Test Collection Rules - No Matches
|
||||
type: http
|
||||
seq: 4
|
||||
}
|
||||
|
||||
post {
|
||||
url: {{baseUrl}}/api/collections/test-rules
|
||||
body: json
|
||||
auth: inherit
|
||||
}
|
||||
|
||||
headers {
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer {{authToken}}
|
||||
}
|
||||
|
||||
body:json {
|
||||
{
|
||||
"rules": [
|
||||
{
|
||||
"field": "genre",
|
||||
"operator": "equals",
|
||||
"value": "NonExistentGenre123456"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
script:post-response {
|
||||
function onResponse(res) {
|
||||
if (res.getStatus() === 200) {
|
||||
const body = res.getBody();
|
||||
tests['No matches returned'] = body.total === 0;
|
||||
tests['Empty matches array'] = body.matches.length === 0;
|
||||
tests['Success with zero results'] = true;
|
||||
}
|
||||
}
|
||||
onResponse(res);
|
||||
}
|
||||
|
||||
settings {
|
||||
encodeUrl: true
|
||||
timeout: 0
|
||||
}
|
||||
|
||||
docs {
|
||||
## Test Collection Rules - No Matches
|
||||
|
||||
Tests behavior when collection rules don't match any books in the library.
|
||||
|
||||
**Example Use Case:** Validating that a new genre name doesn't exist before creating a collection for it, or testing edge cases.
|
||||
|
||||
**Expected Result:** 200 OK with empty matches array and total: 0
|
||||
|
||||
**Purpose:** Verifies that the API handles zero-match scenarios gracefully:
|
||||
- Returns 200 (success) not 404
|
||||
- Returns empty array, not null
|
||||
- Returns total: 0 for clarity
|
||||
- No errors thrown for no results
|
||||
|
||||
**Note:** An empty result set is a valid response and doesn't indicate an error. This allows users to test rules confidently before creating collections.
|
||||
}
|
||||
@@ -1,83 +1,119 @@
|
||||
meta {
|
||||
name: "Test Collection Rules"
|
||||
name: Test Collection Rules
|
||||
type: http
|
||||
seq: 1
|
||||
}
|
||||
|
||||
post {
|
||||
url: {{baseUrl}}/api/collections/test-rules
|
||||
body: {
|
||||
rules: [
|
||||
body: json
|
||||
auth: inherit
|
||||
}
|
||||
|
||||
headers {
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer {{authToken}}
|
||||
}
|
||||
|
||||
body:json {
|
||||
{
|
||||
"rules": [
|
||||
{
|
||||
field: "genre",
|
||||
operator: "equals",
|
||||
value: "Science Fiction"
|
||||
"field": "genre",
|
||||
"operator": "equals",
|
||||
"value": "Science Fiction"
|
||||
}
|
||||
]
|
||||
}
|
||||
assert {
|
||||
res.status: 200
|
||||
res.body.matches: #array
|
||||
res.body.total: #number
|
||||
}
|
||||
}
|
||||
|
||||
post {
|
||||
url: {{baseUrl}}/api/collections/test-rules
|
||||
body: {
|
||||
rules: [
|
||||
script:post-response {
|
||||
function onResponse(res) {
|
||||
if (res.getStatus() === 200) {
|
||||
tests['Rules test successful'] = true;
|
||||
const body = res.getBody();
|
||||
tests['Has matches array'] = Array.isArray(body.matches);
|
||||
tests['Has total count'] = body.total !== undefined;
|
||||
} else {
|
||||
tests['Rules test failed'] = false;
|
||||
}
|
||||
}
|
||||
onResponse(res);
|
||||
}
|
||||
|
||||
settings {
|
||||
encodeUrl: true
|
||||
timeout: 0
|
||||
}
|
||||
|
||||
docs {
|
||||
## Test Collection Rules
|
||||
|
||||
Tests collection rules against the library to see which books match, without creating a collection. Useful for previewing what books would be included in a collection with specific rules.
|
||||
|
||||
**Method:** POST
|
||||
|
||||
**Endpoint:** /api/collections/test-rules
|
||||
|
||||
**Authentication:** Bearer token
|
||||
|
||||
**Request Body:**
|
||||
- `rules` (array): Array of rule objects to test
|
||||
- `field` (string): Field to test (genre, author, copyright_year, tags, etc.)
|
||||
- `operator` (string): Comparison operator
|
||||
- `equals`: Exact match
|
||||
- `contains`: Contains substring (for text fields)
|
||||
- `greater_than`: Greater than (for numeric fields)
|
||||
- `less_than`: Less than (for numeric fields)
|
||||
- `not_equals`: Not equal to
|
||||
- `starts_with`: Starts with
|
||||
- `ends_with`: Ends with
|
||||
- `is_empty`: Field is empty or null
|
||||
- `is_not_empty`: Field is not empty and not null
|
||||
- `value` (string): Value to compare against (not required for is_empty/is_not_empty)
|
||||
|
||||
**Response:**
|
||||
- `matches` (array): Array of matching books
|
||||
- `id` (string): Book UUID
|
||||
- `title` (string): Book title
|
||||
- `author` (string): Book author
|
||||
- `genre` (string): Book genre
|
||||
- Additional book metadata
|
||||
- `total` (number): Total number of matching books
|
||||
|
||||
**Status Codes:**
|
||||
- 200: Success - returns matching books
|
||||
- 400: Invalid request (empty rules array, invalid field/operator)
|
||||
- 401: Unauthorized
|
||||
- 500: Internal server error
|
||||
|
||||
**Example Request:**
|
||||
```json
|
||||
{
|
||||
"rules": [
|
||||
{
|
||||
field: "author",
|
||||
operator: "contains",
|
||||
value: "Asimov"
|
||||
"field": "genre",
|
||||
"operator": "equals",
|
||||
"value": "Science Fiction"
|
||||
}
|
||||
]
|
||||
}
|
||||
assert {
|
||||
res.status: 200
|
||||
res.body.matches: #array
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
post {
|
||||
url: {{baseUrl}}/api/collections/test-rules
|
||||
body: {
|
||||
rules: [
|
||||
**Example Response:**
|
||||
```json
|
||||
{
|
||||
"matches": [
|
||||
{
|
||||
field: "copyright_year",
|
||||
operator: "greater_than",
|
||||
value: "2000"
|
||||
"id": "book-uuid-1",
|
||||
"title": "Foundation",
|
||||
"author": "Isaac Asimov",
|
||||
"genre": "Science Fiction"
|
||||
}
|
||||
]
|
||||
],
|
||||
"total": 1
|
||||
}
|
||||
assert {
|
||||
res.status: 200
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
post {
|
||||
url: {{baseUrl}}/api/collections/test-rules
|
||||
body: {
|
||||
rules: [
|
||||
{
|
||||
field: "genre",
|
||||
operator: "equals",
|
||||
value: "NonExistentGenre123456"
|
||||
}
|
||||
]
|
||||
}
|
||||
assert {
|
||||
res.status: 200
|
||||
res.body.total: 0
|
||||
}
|
||||
}
|
||||
|
||||
post {
|
||||
url: {{baseUrl}}/api/collections/test-rules
|
||||
body: {
|
||||
rules: []
|
||||
}
|
||||
assert {
|
||||
res.status: 400
|
||||
}
|
||||
**Note:** This endpoint is useful for validating collection rules before creating a collection, or for dynamically querying books based on criteria.
|
||||
}
|
||||
|
||||
@@ -7,12 +7,9 @@ meta {
|
||||
put {
|
||||
url: {{base_url}}/api/collections/{{collection_id}}
|
||||
body: json
|
||||
auth: bearer
|
||||
auth: inherit
|
||||
}
|
||||
|
||||
auth:bearer {
|
||||
token: {{token}}
|
||||
}
|
||||
|
||||
body:json {
|
||||
{
|
||||
|
||||
@@ -7,12 +7,9 @@ meta {
|
||||
put {
|
||||
url: {{base_url}}/api/devices/{{device_id}}/collections/{{mapping_id}}
|
||||
body: json
|
||||
auth: bearer
|
||||
auth: inherit
|
||||
}
|
||||
|
||||
auth:bearer {
|
||||
token: {{token}}
|
||||
}
|
||||
|
||||
body:json {
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user