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:
2026-02-08 20:49:06 -05:00
parent cb76b9ca05
commit 3117ce54ec
93 changed files with 3974 additions and 1576 deletions
+81 -37
View File
@@ -1,40 +1,84 @@
{
"meta": {
"name": "Bulk Dismiss Conflicts",
"type": "http",
"event": [
{
"listen": "test",
"script": {
"exec": [
"// Test bulk dismiss response",
"if (response.status === 200) {",
" tests['Bulk dismiss successful'] = true;",
" const body = JSON.parse(response.body);",
" tests['Has results array'] = Array.isArray(body.results);",
" tests['Has total count'] = body.total !== undefined;",
" tests['Has success count'] = body.success !== undefined;",
" tests['Has failed count'] = body.failed !== undefined;",
"} else {",
" tests['Bulk dismiss failed'] = false;",
"}"
]
}
}
meta {
name: Bulk Dismiss Conflicts
type: http
seq: 1
}
post {
url: {{baseUrl}}/api/conflicts/bulk-dismiss
body: json
auth: inherit
}
headers {
Content-Type: application/json
Authorization: Bearer {{authToken}}
}
body:json {
{
"conflict_ids": [
"{{conflictId1}}",
"{{conflictId2}}"
]
},
"req": {
"url": "{{baseUrl}}/api/conflicts/bulk-dismiss",
"method": "POST",
"headers": {
"Content-Type": "application/json",
"Authorization": "Bearer {{authToken}}"
},
"body": {
"conflict_ids": [
"{{conflictId1}}",
"{{conflictId2}}"
]
}
}
}
script:post-response {
function onResponse(res) {
if (res.getStatus() === 200) {
tests['Bulk dismiss successful'] = true;
const body = res.getBody();
tests['Has results array'] = Array.isArray(body.results);
tests['Has total count'] = body.total !== undefined;
tests['Has success count'] = body.success !== undefined;
tests['Has failed count'] = body.failed !== undefined;
} else {
tests['Bulk dismiss failed'] = false;
}
}
onResponse(res);
}
settings {
encodeUrl: true
timeout: 0
}
docs {
## Bulk Dismiss Conflicts
Dismisses multiple sync conflicts without resolving them. This removes them from the conflict list while leaving the data unchanged.
**Method:** POST
**Endpoint:** /api/conflicts/bulk-dismiss
**Authentication:** Bearer token
**Request Body:**
- `conflict_ids` (array): Array of conflict UUIDs to dismiss
**Response:**
- `results` (array): Results for each dismissal
- `total` (number): Total number of conflicts processed
- `success` (number): Number of successfully dismissed conflicts
- `failed` (number): Number of failed dismissals
**Status Codes:**
- 200: Success
- 400: Invalid request data
- 401: Unauthorized
- 403: Forbidden
- 404: One or more conflicts not found
- 500: Internal server error
**Example:**
```json
{
"conflict_ids": ["uuid-1", "uuid-2"]
}
```
**Note:** Dismissing a conflict removes it from the conflict list but does not merge or resolve the conflicting data. Use this when you want to ignore a conflict and handle it manually.
}
+91 -41
View File
@@ -1,43 +1,93 @@
{
"meta": {
"name": "Bulk Resolve Conflicts",
"type": "http",
"event": [
{
"listen": "test",
"script": {
"exec": [
"// Test bulk resolution response",
"if (response.status === 200) {",
" tests['Bulk resolve successful'] = true;",
" const body = JSON.parse(response.body);",
" tests['Has results array'] = Array.isArray(body.results);",
" tests['Has total count'] = body.total !== undefined;",
" tests['Has success count'] = body.success !== undefined;",
" tests['Has failed count'] = body.failed !== undefined;",
" tests['Total equals sum of success and failed'] = body.total === body.success + body.failed;",
"} else {",
" tests['Bulk resolve failed'] = false;",
"}"
]
}
}
]
},
"req": {
"url": "{{baseUrl}}/api/conflicts/bulk-resolve",
"method": "POST",
"headers": {
"Content-Type": "application/json",
"Authorization": "Bearer {{authToken}}"
},
"body": {
"conflict_ids": [
"{{conflictId1}}",
"{{conflictId2}}",
"{{conflictId3}}"
],
"strategy": "most_recent"
}
meta {
name: Bulk Resolve Conflicts
type: http
seq: 1
}
post {
url: {{baseUrl}}/api/conflicts/bulk-resolve
body: json
auth: inherit
}
headers {
Content-Type: application/json
Authorization: Bearer {{authToken}}
}
body:json {
{
"conflict_ids": [
"{{conflictId1}}",
"{{conflictId2}}",
"{{conflictId3}}"
],
"strategy": "most_recent"
}
}
script:post-response {
function onResponse(res) {
if (res.getStatus() === 200) {
tests['Bulk resolve successful'] = true;
const body = res.getBody();
tests['Has results array'] = Array.isArray(body.results);
tests['Has total count'] = body.total !== undefined;
tests['Has success count'] = body.success !== undefined;
tests['Has failed count'] = body.failed !== undefined;
tests['Total equals sum of success and failed'] = body.total === body.success + body.failed;
} else {
tests['Bulk resolve failed'] = false;
}
}
onResponse(res);
}
settings {
encodeUrl: true
timeout: 0
}
docs {
## Bulk Resolve Conflicts
Resolves multiple sync conflicts in a single request using a specified resolution strategy.
**Method:** POST
**Endpoint:** /api/conflicts/bulk-resolve
**Authentication:** Bearer token
**Request Body:**
- `conflict_ids` (array): Array of conflict UUIDs to resolve
- `strategy` (string): Resolution strategy
- `most_recent`: Use the most recently updated progress
- `highest_progress`: Use the reading progress with the highest percent read
- `server`: Always prefer server-side data
- `device`: Always prefer device-side data
**Response:**
- `results` (array): Results for each conflict resolution
- `total` (number): Total number of conflicts processed
- `success` (number): Number of successfully resolved conflicts
- `failed` (number): Number of failed resolutions
**Status Codes:**
- 200: Success (with partial results if some failed)
- 400: Invalid request data
- 401: Unauthorized
- 403: Forbidden
- 404: One or more conflicts not found
- 500: Internal server error
**Example:**
```json
{
"conflict_ids": ["uuid-1", "uuid-2", "uuid-3"],
"strategy": "most_recent"
}
```
**Note:** Conflicts are resolved atomically per conflict. If one resolution fails, others may still succeed.
}
@@ -1,37 +1,74 @@
{
"meta": {
"name": "Bulk Resolve with Highest Progress Strategy",
"type": "http",
"event": [
{
"listen": "test",
"script": {
"exec": [
"// Test highest progress strategy",
"if (response.status === 200) {",
" tests['Highest progress strategy successful'] = true;",
" const body = JSON.parse(response.body);",
" tests['At least one conflict resolved'] = body.success > 0;",
"} else {",
" tests['Strategy failed'] = false;",
"}"
]
}
}
]
},
"req": {
"url": "{{baseUrl}}/api/conflicts/bulk-resolve",
"method": "POST",
"headers": {
"Content-Type": "application/json",
"Authorization": "Bearer {{authToken}}"
},
"body": {
"conflict_ids": [
"{{conflictId1}}"
],
"strategy": "highest_progress"
}
meta {
name: Bulk Resolve with Highest Progress Strategy
type: http
seq: 1
}
post {
url: {{baseUrl}}/api/conflicts/bulk-resolve
body: json
auth: inherit
}
headers {
Content-Type: application/json
Authorization: Bearer {{authToken}}
}
body:json {
{
"conflict_ids": [
"{{conflictId1}}"
],
"strategy": "highest_progress"
}
}
script:post-response {
function onResponse(res) {
if (res.getStatus() === 200) {
tests['Highest progress strategy successful'] = true;
const body = res.getBody();
tests['At least one conflict resolved'] = body.success > 0;
} else {
tests['Strategy failed'] = false;
}
}
onResponse(res);
}
settings {
encodeUrl: true
timeout: 0
}
docs {
## Bulk Resolve with Highest Progress Strategy
Resolves multiple sync conflicts using the "highest_progress" strategy, which keeps the reading progress with the highest percentage read.
**Method:** POST
**Endpoint:** /api/conflicts/bulk-resolve
**Authentication:** Bearer token
**Request Body:**
- `conflict_ids` (array): Array of conflict UUIDs to resolve
- `strategy` (string): Must be "highest_progress"
**Response:**
- `results` (array): Results for each conflict resolution
- `total` (number): Total number of conflicts processed
- `success` (number): Number of successfully resolved conflicts
- `failed` (number): Number of failed resolutions
**Status Codes:**
- 200: Success
- 400: Invalid request data
- 401: Unauthorized
- 403: Forbidden
- 500: Internal server error
**Note:** The highest progress strategy is ideal when you want to preserve the most reading progress across devices. Use this when you've been reading on multiple devices and want to keep the furthest position.
}
+35 -18
View File
@@ -7,27 +7,44 @@ meta {
delete {
url: {{baseUrl}}/api/conflicts/{{conflict_id}}
body: none
auth: bearer
auth: inherit
}
headers: {
headers {
Authorization: Bearer {{token}}
}
docs: {
Deletes a specific conflict record.
Path Parameters:
- conflict_id: UUID of the conflict to delete
Use this when:
- A conflict was created in error
- You want to dismiss a conflict without resolving it
- The conflict is no longer relevant
Response: 204 No Content on success
Note: This permanently removes the conflict record.
Consider resolving the conflict instead if you want to
maintain an audit trail of what happened.
settings {
encodeUrl: true
timeout: 0
}
docs {
## Delete Conflict
Permanently deletes a specific conflict record from the system.
**Method:** DELETE
**Endpoint:** /api/conflicts/{conflict_id}
**Authentication:** Bearer token
**Path Parameters:**
- `conflict_id` (string): Conflict UUID to delete
**Response:** 204 No Content on success
**Status Codes:**
- 204: Success - conflict deleted
- 401: Unauthorized
- 404: Conflict not found
- 500: Internal server error
**Use Cases:**
- Conflict was created in error
- Dismissing a conflict without resolving it
- Conflict is no longer relevant (e.g., book deleted)
**Note:** This permanently removes the conflict record with no undo option. Consider resolving the conflict instead if you want to maintain an audit trail of what happened.
}
+36 -17
View File
@@ -7,29 +7,48 @@ meta {
post {
url: {{baseUrl}}/api/conflicts/dismiss-all
body: none
auth: bearer
auth: inherit
}
headers: {
headers {
Authorization: Bearer {{token}}
}
docs: {
Deletes all resolved conflicts for the authenticated user.
Use this to:
- Clean up your conflicts list after reviewing resolutions
- Remove old resolved conflicts that are no longer needed
- Maintain a clean conflict history
Response includes:
- deleted: Number of conflict records that were deleted
Example response:
settings {
encodeUrl: true
timeout: 0
}
docs {
## Dismiss All Resolved Conflicts
Deletes all resolved conflicts for the authenticated user, cleaning up the conflict list.
**Method:** POST
**Endpoint:** /api/conflicts/dismiss-all
**Authentication:** Bearer token
**Response:**
- `deleted` (number): Number of conflict records that were deleted
**Status Codes:**
- 200: Success - conflicts deleted
- 401: Unauthorized
- 500: Internal server error
**Example Response:**
```json
{
"deleted": 5
}
Note: Only resolves conflicts with status "user_resolved"
are deleted. Unresolved conflicts are preserved.
```
**Use Cases:**
- Clean up conflicts list after reviewing resolutions
- Remove old resolved conflicts no longer needed
- Maintain a clean conflict history
**Note:** Only conflicts with status "user_resolved" or "auto_resolved" are deleted. Unresolved conflicts are preserved.
}
+74 -39
View File
@@ -7,52 +7,87 @@ meta {
get {
url: {{baseUrl}}/api/conflicts/{{conflict_id}}
body: none
auth: bearer
auth: inherit
}
headers: {
headers {
Authorization: Bearer {{token}}
}
docs: {
Retrieves detailed information about a specific conflict.
Path Parameters:
- conflict_id: UUID of the conflict
Response includes:
- id: Conflict UUID
- media_item_id: Associated book UUID
- media_item_title: Book title
- conflict_type: Type of conflict
- conflict_data: Side-by-side comparison with sources:
* source: Device/source identifier (koreader, kobo, web, etc.)
* timestamp: When this progress was recorded
* data: The conflicting data (percentage, epubcfi, chapter, etc.)
- resolution_status: Current status
- resolution_data: If resolved, includes resolution details
- resolved_by: User ID who resolved it (if applicable)
- resolved_at: When it was resolved (if applicable)
- created_at: When conflict was detected
Example conflict_data:
settings {
encodeUrl: true
timeout: 0
}
docs {
## Get Conflict Details
Retrieves detailed information about a specific conflict, including side-by-side comparison of conflicting data from all sources.
**Method:** GET
**Endpoint:** /api/conflicts/{conflict_id}
**Authentication:** Bearer token
**Path Parameters:**
- `conflict_id` (string): Conflict UUID
**Response:**
- `id` (string): Conflict UUID
- `media_item_id` (string): Associated book UUID
- `media_item_title` (string): Book title
- `conflict_type` (string): Type of conflict (progress, note, highlight)
- `conflict_data` (object): Side-by-side comparison from each source
- Each source includes:
- `source` (string): Device/source identifier (koreader, kobo, web, etc.)
- `timestamp` (string): When this data was recorded
- `data` (object): The conflicting data
- `percentage` (number): Reading progress
- `epubcfi` (string): EPUB location
- `chapter` (number): Chapter number
- `resolution_status` (string): Current status (unresolved, user_resolved, auto_resolved)
- `resolution_data` (object, optional): If resolved, includes resolution details
- `resolved_by` (string, optional): User ID who resolved it
- `resolved_at` (string, optional): When it was resolved
- `created_at` (string): When conflict was detected
**Status Codes:**
- 200: Success
- 401: Unauthorized
- 404: Conflict not found
- 500: Internal server error
**Example Response:**
```json
{
"koreader": {
"source": "koreader",
"timestamp": "2026-01-30T20:10:00Z",
"data": {
"percentage": 0.45,
"epubcfi": "epubcfi(/6/4/2:15)",
"chapter": 3
"id": "conflict-uuid",
"media_item_id": "book-uuid",
"media_item_title": "Foundation",
"conflict_type": "progress",
"conflict_data": {
"koreader": {
"source": "koreader",
"timestamp": "2026-01-30T20:10:00Z",
"data": {
"percentage": 0.45,
"epubcfi": "epubcfi(/6/4/2:15)",
"chapter": 3
}
},
"kobo": {
"source": "kobo",
"timestamp": "2026-01-30T20:05:00Z",
"data": {
"percentage": 0.42,
"location": "unknown"
}
}
},
"kobo": {
"source": "kobo",
"timestamp": "2026-01-30T20:05:00Z",
"data": {
"percentage": 0.42,
"location": "unknown"
}
}
"resolution_status": "unresolved",
"created_at": "2026-01-30T20:10:00Z"
}
```
**Note:** Use this to get full details before resolving, showing exactly what data differs between sources.
}
+79 -21
View File
@@ -7,30 +7,88 @@ meta {
get {
url: {{baseUrl}}/api/conflicts?status=unresolved
body: none
auth: bearer
auth: inherit
}
headers: {
headers {
Authorization: Bearer {{token}}
}
docs: {
Lists all sync conflicts for the authenticated user.
Query Parameters:
- status: Filter by resolution status (unresolved, user_resolved, auto_resolved, all)
Response includes:
- conflicts: Array of conflict details
- total: Total number of conflicts
- unresolved: Number of unresolved conflicts
Each conflict includes:
- id: Conflict UUID
- media_item_id: Associated book UUID
- media_item_title: Book title
- conflict_type: Type of conflict (progress, note, highlight)
- conflict_data: Side-by-side comparison of conflicting data
- resolution_status: Current status
- created_at: When conflict was detected
settings {
encodeUrl: true
timeout: 0
}
docs {
## List Conflicts
Lists all sync conflicts for the authenticated user with optional filtering.
**Method:** GET
**Endpoint:** /api/conflicts
**Authentication:** Bearer token
**Query Parameters:**
- `status` (string, optional): Filter by resolution status
- `unresolved`: Only unresolved conflicts (default)
- `user_resolved`: Conflicts resolved by user
- `auto_resolved`: Automatically resolved conflicts
- `all`: All conflicts regardless of status
- `type` (string, optional): Filter by conflict type
- `progress`: Reading progress conflicts
- `note`: Bookmark/note conflicts
- `highlight`: Highlight conflicts
**Response:**
- `conflicts` (array): Array of conflict objects
- `total` (number): Total number of conflicts matching filters
- `unresolved` (number): Number of unresolved conflicts
**Each Conflict Object:**
- `id` (string): Conflict UUID
- `media_item_id` (string): Associated book UUID
- `media_item_title` (string): Book title
- `conflict_type` (string): Type of conflict (progress, note, highlight)
- `conflict_data` (object): Side-by-side comparison of conflicting data
- `koreader`: Data from KOReader device
- `kobo`: Data from Kobo device
- `web`: Data from web interface
- `resolution_status` (string): Current status (unresolved, user_resolved, auto_resolved)
- `created_at` (string): ISO 8601 timestamp when conflict was detected
**Status Codes:**
- 200: Success
- 401: Unauthorized
- 500: Internal server error
**Example Request:**
```
GET /api/conflicts?status=unresolved&type=progress
```
**Example Response:**
```json
{
"conflicts": [
{
"id": "conflict-uuid",
"media_item_id": "book-uuid",
"media_item_title": "Foundation",
"conflict_type": "progress",
"conflict_data": {
"koreader": { "percentage": 0.65, "epubcfi": "..." },
"kobo": { "percentage": 0.43, "epubcfi": "..." }
},
"resolution_status": "unresolved",
"created_at": "2026-01-31T12:00:00Z"
}
],
"total": 1,
"unresolved": 1
}
```
**Note:** Conflicts occur when multiple devices update the same book data without syncing first.
}
+64 -27
View File
@@ -7,10 +7,10 @@ meta {
post {
url: {{baseUrl}}/api/conflicts/{{conflict_id}}/resolve
body: json
auth: bearer
auth: inherit
}
headers: {
headers {
Authorization: Bearer {{token}}
Content-Type: application/json
}
@@ -24,25 +24,63 @@ body:json {
}
}
docs: {
Resolves a sync conflict by choosing which source to use.
Path Parameters:
- conflict_id: UUID of the conflict to resolve
Request Body:
- winner: Source to choose (koreader, kobo, web, manual)
- manual_data: Required if winner is "manual" - contains the merged data
- apply_to_all_future_conflicts: Whether to auto-resolve future conflicts from this source
- reason: Optional explanation for the resolution
Example request body for choosing koreader:
settings {
encodeUrl: true
timeout: 0
}
docs {
## Resolve Conflict
Resolves a sync conflict by choosing which source to use for the conflicting data.
**Method:** POST
**Endpoint:** /api/conflicts/{conflict_id}/resolve
**Authentication:** Bearer token
**Path Parameters:**
- `conflict_id` (string): Conflict UUID
**Request Body:**
- `winner` (string): Source to choose
- `koreader`: Use KOReader device data
- `kobo`: Use Kobo device data
- `web`: Use web interface data
- `manual`: Use custom merged data (requires manual_data)
- `manual_data` (object, optional): Required if winner is "manual"
- `percentage` (number): Reading progress percentage (0-1)
- `epubcfi` (string): EPUB Canonical Fragment Identifier
- `chapter` (number, optional): Chapter number
- `page` (number, optional): Page number
- `apply_to_all_future_conflicts` (boolean): Auto-resolve future conflicts from this source
- `reason` (string, optional): Explanation for the resolution choice
**Response:**
- `conflict_resolved` (boolean): True if successful
- `applied_to` (string): What was updated (progress, annotations, etc.)
- `devices_synced` (array): List of device IDs that were notified
**Status Codes:**
- 200: Success - conflict resolved
- 400: Invalid request data
- 401: Unauthorized
- 404: Conflict not found
- 500: Internal server error
**Example - Choose KOReader:**
```json
{
"winner": "koreader",
"manual_data": null,
"apply_to_all_future_conflicts": false,
"reason": "More recent progress"
}
Example request body for manual resolution:
```
**Example - Manual Override:**
```json
{
"winner": "manual",
"manual_data": {
@@ -50,17 +88,16 @@ docs: {
"epubcfi": "epubcfi(/6/4/2:20)",
"chapter": 3
},
"apply_to_all_future_conflicts": false,
"reason": "Custom merged position"
}
Response includes:
- conflict_resolved: true if successful
- applied_to: What was updated (progress, annotations)
- devices_synced: List of device IDs that were notified
After resolution:
- The winning data is applied to the reading progress
- All connected devices are notified via WebSocket
```
**After Resolution:**
- Winning data is applied to reading progress
- All connected devices notified via WebSocket
- Conflict status changes to "user_resolved"
- Resolution data is stored for audit trail
- Resolution stored for audit trail
**Note:** Manual override allows precise control when automatic resolution doesn't capture the correct state.
}
-127
View File
@@ -1,127 +0,0 @@
meta {
name: "Bookhoard Conflicts API"
type: "collection"
environment: {
development: {
base_url: "http://localhost:8765/api"
},
production: {
base_url: "https://your-domain.com/api"
}
}
}
# List Conflicts
@name("List Unresolved Conflicts")
GET {{environment.base_url}}/conflicts?status=unresolved
Authorization: Bearer {{jwt_token}}
@name("List All Conflicts")
GET {{environment.base_url}}/conflicts?status=all
Authorization: Bearer {{jwt_token}}
@name("List Resolved Conflicts")
GET {{environment.base_url}}/conflicts?status=resolved
Authorization: Bearer {{jwt_token}}
@name("List Progress Conflicts")
GET {{environment.base_url}}/conflicts?status=unresolved&type=progress
Authorization: Bearer {{jwt_token}}
@name("List Note Conflicts")
GET {{environment.base_url}}/conflicts?status=unresolved&type=note
Authorization: Bearer {{jwt_token}}
@name("List Highlight Conflicts")
GET {{environment.base_url}}/conflicts?status=unresolved&type=highlight
Authorization: Bearer {{jwt_token}}
# Get Conflict Details
@name("Get Conflict Details")
GET {{environment.base_url}}/conflicts/{{conflict_id}}
Authorization: Bearer {{jwt_token}}
# Resolve Conflict
@name("Resolve - Keep KOReader Progress")
POST {{environment.base_url}}/conflicts/{{conflict_id}}/resolve
Authorization: Bearer {{jwt_token}}
Content-Type: application/json
{
"winner": "koreader",
"apply_to_all_future_conflicts": false,
"reason": "KOReader was most recently updated"
}
@name("Resolve - Keep Kobo Progress")
POST {{environment.base_url}}/conflicts/{{conflict_id}}/resolve
Authorization: Bearer {{jwt_token}}
Content-Type: application/json
{
"winner": "kobo",
"apply_to_all_future_conflicts": false,
"reason": "Kobo has more progress"
}
@name("Resolve - Keep Web Progress")
POST {{environment.base_url}}/conflicts/{{conflict_id}}/resolve
Authorization: Bearer {{jwt_token}}
Content-Type: application/json
{
"winner": "web",
"apply_to_all_future_conflicts": false,
"reason": "Web interface has most recent data"
}
@name("Resolve - Manual Override")
POST {{environment.base_url}}/conflicts/{{conflict_id}}/resolve
Authorization: Bearer {{jwt_token}}
Content-Type: application/json
{
"winner": "manual",
"manual_data": {
"percentage": 0.43,
"epubcfi": "epubcfi(/6/4/2:20)",
"chapter": 3
},
"apply_to_all_future_conflicts": false,
"reason": "User specified custom progress"
}
@name("Resolve - Manual Override with Page")
POST {{environment.base_url}}/conflicts/{{conflict_id}}/resolve
Authorization: Bearer {{jwt_token}}
Content-Type: application/json
{
"winner": "manual",
"manual_data": {
"percentage": 0.43,
"page": 89,
"epubcfi": "epubcfi(/6/4/2:20)"
},
"apply_to_all_future_conflicts": false,
"reason": "User specified page number"
}
@name("Resolve - Apply to All Future Conflicts")
POST {{environment.base_url}}/conflicts/{{conflict_id}}/resolve
Authorization: Bearer {{jwt_token}}
Content-Type: application/json
{
"winner": "koreader",
"apply_to_all_future_conflicts": true,
"reason": "Always prefer KOReader for future conflicts"
}
# Delete Conflict
@name("Delete Conflict")
DELETE {{environment.base_url}}/conflicts/{{conflict_id}}
Authorization: Bearer {{jwt_token}}
@name("Dismiss Conflict")
DELETE {{environment.base_url}}/conflicts/{{conflict_id}}
Authorization: Bearer {{jwt_token}}
# Batch Operations
@name("Dismiss All Resolved Conflicts")
DELETE {{environment.base_url}}/conflicts/dismiss-resolved
Authorization: Bearer {{jwt_token}}