docs: align swagger spec and README with Ombi features and blocklist eligibility (closes #37)
Build and Push Docker Image / build (push) Successful in 38s
Docs Check / Markdown lint (push) Successful in 1m18s
Licence Check / Licence compatibility and copyright header verification (push) Failing after 2m11s
CI / Security audit (push) Successful in 2m27s
Docs Check / Mermaid diagram parse check (push) Successful in 3m5s
CI / Swagger Validation & Coverage (push) Successful in 3m19s
CI / Tests & coverage (push) Successful in 3m34s
Build and Push Docker Image / build (push) Successful in 38s
Docs Check / Markdown lint (push) Successful in 1m18s
Licence Check / Licence compatibility and copyright header verification (push) Failing after 2m11s
CI / Security audit (push) Successful in 2m27s
Docs Check / Mermaid diagram parse check (push) Successful in 3m5s
CI / Swagger Validation & Coverage (push) Successful in 3m19s
CI / Tests & coverage (push) Successful in 3m34s
This commit is contained in:
+8
-3
@@ -62,7 +62,7 @@ flowchart TB
|
||||
auth_r["Auth Routes\n/api/auth"]
|
||||
dash_r["Dashboard Routes\n/api/dashboard (SSE /stream)"]
|
||||
stat_r["Status Routes\n/api/status"]
|
||||
wh_r["Webhook Routes\n/api/webhook/sonarr|radarr"]
|
||||
wh_r["Webhook Routes\n/api/webhook/sonarr|radarr|ombi"]
|
||||
ombi_r["Ombi Routes\n/api/ombi"]
|
||||
hist_r["History Routes\n/api/history"]
|
||||
proxy_r["Proxy Routes\n/api/sonarr · /api/radarr\n/api/sabnzbd · /api/emby"]
|
||||
@@ -119,7 +119,7 @@ flowchart TB
|
||||
Browser (SPA)
|
||||
│ POST /api/auth/login → Auth routes → Emby verify → set httpOnly cookie
|
||||
│ GET /api/dashboard/stream → SSE stream → cache → matched downloads
|
||||
│ POST /api/webhook/* ← Sonarr/Radarr push events
|
||||
│ POST /api/webhook/* ← Sonarr/Radarr/Ombi push events
|
||||
│
|
||||
▼
|
||||
Express Server (:3001)
|
||||
@@ -132,10 +132,12 @@ Express Server (:3001)
|
||||
├── /api/auth → login, logout, me, csrf
|
||||
├── /api/webhook → [rate-limit] → [secret validation] → [payload validation]
|
||||
│ → [replay check] → updateWebhookMetrics → processWebhookEvent
|
||||
│ → /config: GET endpoint for configuration status validation
|
||||
├── /api/dashboard → requireAuth → read cache → DownloadBuilder → SSE/JSON
|
||||
├── /api/status → requireAuth → admin cache/polling/webhook status
|
||||
├── /api/history → requireAuth → historyFetcher (5 min cache) → filter + dedup
|
||||
├── /api/ombi → requireAuth → PALDRA → filter/sort/search → JSON
|
||||
│ → /webhook/*: enable (POST), status (GET), and test (POST) endpoints
|
||||
├── /api/sonarr|radarr → requireAuth → verifyCsrf → proxy to *arr API
|
||||
└── /api/sabnzbd|emby → requireAuth → verifyCsrf → proxy
|
||||
|
||||
@@ -380,11 +382,12 @@ Unmatched torrents are **not** included in the response (fixed in develop-refact
|
||||
|
||||
### 4.1 Webhook Receiver
|
||||
|
||||
sofarr exposes two webhook endpoints that Sonarr and Radarr can be configured to call on every automation event:
|
||||
sofarr exposes three webhook endpoints that Sonarr, Radarr, and Ombi can be configured to call on automation and request events:
|
||||
|
||||
```
|
||||
POST /api/webhook/sonarr
|
||||
POST /api/webhook/radarr
|
||||
POST /api/webhook/ombi
|
||||
```
|
||||
|
||||
Both endpoints share identical processing logic:
|
||||
@@ -468,6 +471,8 @@ The dashboard therefore receives fresh data within the round-trip time of the *a
|
||||
|
||||
The `sonarr.js` and `radarr.js` route modules expose endpoints (under `/api/sonarr` and `/api/radarr` respectively, behind `requireAuth` + `verifyCsrf`) for one-click webhook configuration. These proxy to the *arr notification API to create, update, or remove the sofarr webhook connection in Sonarr/Radarr, using `SOFARR_BASE_URL` to construct the target URL.
|
||||
|
||||
Similarly, the `ombi.js` route module exposes endpoints under `/api/ombi/webhook/` (including `/enable`, `/status`, and `/test`) to support one-click registration and validation of the Sofarr webhook inside the configured Ombi instance.
|
||||
|
||||
---
|
||||
|
||||
## 5. Data Flow and Real-time Updates
|
||||
|
||||
@@ -320,17 +320,16 @@ OMBI_URL=https://ombi.example.com
|
||||
OMBI_API_KEY=your-ombi-api-key
|
||||
```
|
||||
|
||||
**How it works:**
|
||||
- Downloads are matched to Ombi requests using external IDs (TMDB, TVDB, IMDB)
|
||||
- When a matching request is found, an Ombi icon appears in the download card
|
||||
- Clicking the icon opens the Ombi request page
|
||||
- If no request exists, a search link is provided instead
|
||||
- Integration is fully optional - sofarr works perfectly without Ombi configured
|
||||
|
||||
**External ID Matching:**
|
||||
- **TV Shows**: TVDB ID (primary) → TMDB ID (fallback)
|
||||
- **Movies**: TMDB ID (primary) → IMDB ID (fallback)
|
||||
- Matching is performed automatically using data from Sonarr/Radarr
|
||||
**Features & Architecture:**
|
||||
- **Request Filtering & Search**: Retrieve, search, and filter requests in real-time by status (pending, approved, available, denied), media type (movies, TV shows), or name.
|
||||
- **Dual-Layer Filtering & Persistence**: search and core filters are handled server-side for speed and efficiency, while responsive client-side refinements handle on-the-fly rendering. User filter preferences are persisted locally/session-wide to ensure a consistent experience across page refreshes.
|
||||
- **Real-Time SSE Updates**: Integrates with the Server-Sent Events (SSE) push stream. When requests are created or updated, the dashboard is instantly notified without client-side polling.
|
||||
- **External ID Matching**: Downloads are matched to Ombi requests using external IDs (TMDB, TVDB, IMDB).
|
||||
- **TV Shows**: TVDB ID (primary) → TMDB ID (fallback)
|
||||
- **Movies**: TMDB ID (primary) → IMDB ID (fallback)
|
||||
- Matching is performed automatically using data from Sonarr/Radarr.
|
||||
- **Interactive UI**: When a matching request is found, an Ombi icon appears in the download card to open the request page. If no request exists, a search link is provided instead.
|
||||
- **Fully Optional**: Integration is fully optional — sofarr works perfectly without Ombi configured.
|
||||
|
||||
## Setting Up User Tags
|
||||
|
||||
@@ -445,8 +444,10 @@ The proxy routes (`/api/sonarr/**`, `/api/radarr/**`, `/api/sabnzbd/**`, `/api/e
|
||||
### Webhook Receiver (no user auth — protected by `X-Sofarr-Webhook-Secret`)
|
||||
- `POST /api/webhook/sonarr` — receive Sonarr webhook events
|
||||
- `POST /api/webhook/radarr` — receive Radarr webhook events
|
||||
- `POST /api/webhook/ombi` — receive Ombi webhook events
|
||||
|
||||
### Webhook Management (requires auth + CSRF)
|
||||
- `GET /api/webhook/config` — get webhook configuration status
|
||||
- `GET /api/sonarr/api/v3/notification` — list Sonarr notification connections
|
||||
- `POST /api/sonarr/api/v3/notification` — create/update Sonarr notification connection
|
||||
- `GET /api/radarr/api/v3/notification` — list Radarr notification connections
|
||||
@@ -455,6 +456,12 @@ The proxy routes (`/api/sonarr/**`, `/api/radarr/**`, `/api/sabnzbd/**`, `/api/e
|
||||
- `POST /api/radarr/webhook/enable` — one-click enable Sofarr webhook in Radarr
|
||||
- `POST /api/sonarr/webhook/test` — trigger a Sonarr test event
|
||||
- `POST /api/radarr/webhook/test` — trigger a Radarr test event
|
||||
- `GET /api/ombi/webhook/status` — get Ombi webhook status and metrics
|
||||
- `POST /api/ombi/webhook/enable` — one-click enable Sofarr webhook in Ombi
|
||||
- `POST /api/ombi/webhook/test` — trigger an Ombi test event
|
||||
|
||||
### Ombi (requires auth)
|
||||
- `GET /api/ombi/requests` — get Ombi requests with server-side filtering, search, and sorting
|
||||
|
||||
### Service APIs (proxy to your services)
|
||||
- `GET /api/sabnzbd/*` — SABnzbd API proxy
|
||||
@@ -499,7 +506,7 @@ npm run test:coverage # with V8 coverage report (outputs to coverage/)
|
||||
npm run test:ui # interactive Vitest UI
|
||||
```
|
||||
|
||||
290 tests across 18 test files covering auth middleware, CSRF protection, secret sanitization, config parsing, token store, qBittorrent utilities, history deduplication/classification, download client architecture, webhook endpoint security, input validation, replay protection, and webhook metrics integration. See [`tests/README.md`](tests/README.md) for design decisions and coverage targets.
|
||||
Over 830 tests across 39 files covering auth middleware, CSRF protection, secret sanitization, config parsing, token store, qBittorrent utilities, history deduplication/classification, download client architecture, webhook endpoint security, input validation, replay protection, webhook metrics integration, polling retrievers, and Ombi filter/search logic. See [`tests/README.md`](tests/README.md) for design decisions and coverage targets.
|
||||
|
||||
## Development
|
||||
|
||||
|
||||
+269
-4
@@ -276,7 +276,6 @@ components:
|
||||
- arrQueueId
|
||||
- arrType
|
||||
- arrInstanceUrl
|
||||
- arrInstanceKey
|
||||
- arrContentId
|
||||
- arrContentType
|
||||
properties:
|
||||
@@ -296,7 +295,7 @@ components:
|
||||
example: "http://sonarr:8989"
|
||||
arrInstanceKey:
|
||||
type: string
|
||||
description: API key for the *arr instance
|
||||
description: API key for the *arr instance. Only required for admin users; non-admin requests resolve the key from server-side configurations using arrInstanceUrl.
|
||||
example: "abc123def456"
|
||||
arrContentId:
|
||||
type: integer
|
||||
@@ -686,7 +685,7 @@ paths:
|
||||
post:
|
||||
tags: [Dashboard]
|
||||
summary: Blocklist and re-search
|
||||
description: Admin-only. Removes queue item with blocklist=true, then triggers new automatic search.
|
||||
description: Removes queue item with blocklist=true, then triggers new automatic search. Accessible by admins, or by non-admins who own the item under specific eligibility conditions (has import issues, or torrent older than 1h and availability < 100%).
|
||||
security:
|
||||
- CookieAuth: []
|
||||
- CsrfToken: []
|
||||
@@ -708,7 +707,7 @@ paths:
|
||||
type: boolean
|
||||
example: true
|
||||
'403':
|
||||
description: Admin access required
|
||||
description: Permission denied (admin or qualifying conditions required)
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
@@ -853,6 +852,72 @@ paths:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
|
||||
/api/webhook/ombi:
|
||||
post:
|
||||
tags: [Webhook]
|
||||
summary: Ombi webhook
|
||||
description: Receives webhook events from Ombi. Requires X-Sofarr-Webhook-Secret header.
|
||||
security: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
responses:
|
||||
'200':
|
||||
description: Event received
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
received:
|
||||
type: boolean
|
||||
example: true
|
||||
'401':
|
||||
description: Invalid or missing secret
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
'400':
|
||||
description: Invalid payload
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
|
||||
/api/webhook/config:
|
||||
get:
|
||||
tags: [Webhook]
|
||||
summary: Get webhook configuration status
|
||||
description: Returns whether the required webhook configuration is properly configured.
|
||||
security:
|
||||
- CookieAuth: []
|
||||
responses:
|
||||
'200':
|
||||
description: Webhook configuration status
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
valid:
|
||||
type: boolean
|
||||
example: true
|
||||
missing:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
example: []
|
||||
'401':
|
||||
description: Unauthorized
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
|
||||
# Sonarr proxy endpoints (detailed in JSDoc)
|
||||
/api/sonarr/queue:
|
||||
get:
|
||||
@@ -1478,3 +1543,203 @@ paths:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
|
||||
# Ombi endpoints
|
||||
/api/ombi/requests:
|
||||
get:
|
||||
tags: [Ombi]
|
||||
summary: Get Ombi requests
|
||||
description: Returns Ombi movie and TV requests. Non-admin users only see their own requests, while admins see all requests. Supports server-side filtering by media type, request status, title search, and sorting.
|
||||
security:
|
||||
- CookieAuth: []
|
||||
parameters:
|
||||
- name: type
|
||||
in: query
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
enum: [movie, tv, all]
|
||||
default: [all]
|
||||
description: Filter by media type. Omit or use `all` for both.
|
||||
style: form
|
||||
explode: true
|
||||
- name: status
|
||||
in: query
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
enum: [pending, approved, available, denied]
|
||||
description: Filter by request status. Omit for all statuses.
|
||||
style: form
|
||||
explode: true
|
||||
- name: sort
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
enum: [requestedDate_desc, requestedDate_asc, title_asc, title_desc]
|
||||
default: requestedDate_desc
|
||||
description: Sort mode.
|
||||
- name: search
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
description: Case-insensitive substring match on title.
|
||||
- name: showAll
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
enum: ['true', 'false']
|
||||
description: Admin only. Show all users' requests.
|
||||
responses:
|
||||
'200':
|
||||
description: Ombi requests retrieved successfully
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
user:
|
||||
type: string
|
||||
isAdmin:
|
||||
type: boolean
|
||||
showAll:
|
||||
type: boolean
|
||||
requests:
|
||||
type: object
|
||||
properties:
|
||||
movie:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/OmbiRequest'
|
||||
tv:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/OmbiRequest'
|
||||
total:
|
||||
type: integer
|
||||
'401':
|
||||
description: Unauthorized
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
|
||||
/api/ombi/webhook/enable:
|
||||
post:
|
||||
tags: [Ombi]
|
||||
summary: Enable Ombi webhook
|
||||
description: Registers or updates the Sofarr webhook in Ombi. Requires authentication and CSRF protection.
|
||||
security:
|
||||
- CookieAuth: []
|
||||
responses:
|
||||
'200':
|
||||
description: Webhook enabled successfully
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
webhookUrl:
|
||||
type: string
|
||||
applicationToken:
|
||||
type: string
|
||||
'400':
|
||||
description: Invalid request or missing configuration
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
'401':
|
||||
description: Unauthorized
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
|
||||
/api/ombi/webhook/status:
|
||||
get:
|
||||
tags: [Ombi]
|
||||
summary: Get Ombi webhook status
|
||||
description: Returns the current Ombi webhook configuration status and metrics.
|
||||
security:
|
||||
- CookieAuth: []
|
||||
responses:
|
||||
'200':
|
||||
description: Webhook status retrieved successfully
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
enabled:
|
||||
type: boolean
|
||||
webhookUrl:
|
||||
type: string
|
||||
nullable: true
|
||||
applicationToken:
|
||||
type: string
|
||||
nullable: true
|
||||
triggers:
|
||||
type: object
|
||||
properties:
|
||||
requestAvailable:
|
||||
type: boolean
|
||||
requestApproved:
|
||||
type: boolean
|
||||
requestDeclined:
|
||||
type: boolean
|
||||
requestPending:
|
||||
type: boolean
|
||||
requestProcessing:
|
||||
type: boolean
|
||||
stats:
|
||||
type: object
|
||||
nullable: true
|
||||
properties:
|
||||
eventsReceived:
|
||||
type: integer
|
||||
pollsSkipped:
|
||||
type: integer
|
||||
lastWebhookTimestamp:
|
||||
type: integer
|
||||
'401':
|
||||
description: Unauthorized
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
|
||||
/api/ombi/webhook/test:
|
||||
post:
|
||||
tags: [Ombi]
|
||||
summary: Test Ombi webhook
|
||||
description: Sends a test webhook event to the Sofarr Ombi webhook endpoint.
|
||||
security:
|
||||
- CookieAuth: []
|
||||
- CsrfToken: []
|
||||
responses:
|
||||
'200':
|
||||
description: Test webhook sent successfully
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
'400':
|
||||
description: Invalid request or missing configuration
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
'401':
|
||||
description: Unauthorized
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
|
||||
+15
-13
@@ -558,15 +558,18 @@ router.get('/stream', requireAuth, async (req, res) => {
|
||||
* tags: [Dashboard]
|
||||
* summary: Blocklist and re-search
|
||||
* description: |
|
||||
* Admin-only endpoint that removes a queue item from Sonarr/Radarr with blocklist=true
|
||||
* (so the release is not grabbed again), then immediately triggers a new automatic search
|
||||
* for the same episode/movie.
|
||||
* Removes a queue item from Sonarr/Radarr with blocklist=true (so the release is not grabbed again),
|
||||
* then immediately triggers a new automatic search for the same episode/movie.
|
||||
*
|
||||
* **Authentication:** Requires valid `emby_user` cookie (admin only) and `X-CSRF-Token` header.
|
||||
* Accessible by admins, or by non-admins who own the item under specific qualifying eligibility conditions:
|
||||
* - The download has import issues OR
|
||||
* - The torrent is older than 1 hour and has availability below 100%
|
||||
*
|
||||
* **Authentication:** Requires valid `emby_user` cookie and `X-CSRF-Token` header.
|
||||
*
|
||||
* **Workflow:**
|
||||
* 1. Validate user is admin
|
||||
* 2. Validate all required fields are present
|
||||
* 1. Validate user and required fields
|
||||
* 2. Check blocklist eligibility (admin status or non-admin qualifying criteria)
|
||||
* 3. Delete queue item from Sonarr/Radarr with `removeFromClient=true` and `blocklist=true`
|
||||
* 4. Trigger automatic search command:
|
||||
* - Sonarr: EpisodeSearch with episodeIds
|
||||
@@ -577,18 +580,17 @@ router.get('/stream', requireAuth, async (req, res) => {
|
||||
* - `arrQueueId`: Sonarr/Radarr queue record ID
|
||||
* - `arrType`: Must be "sonarr" or "radarr"
|
||||
* - `arrInstanceUrl`: Base URL of the *arr instance
|
||||
* - `arrInstanceKey`: API key for the *arr instance
|
||||
* - `arrInstanceKey`: API key for the *arr instance (only required for admins; non-admins resolve via server config)
|
||||
* - `arrContentId`: episodeId (Sonarr) or movieId (Radarr)
|
||||
* - `arrContentType`: Must be "episode" (Sonarr) or "movie" (Radarr)
|
||||
*
|
||||
* **Error Responses:**
|
||||
* - 403: Non-admin user attempts access
|
||||
* - 403: User lacks permissions (admin or qualifying conditions required)
|
||||
* - 400: Missing required fields or invalid arrType
|
||||
* - 502: Failed to communicate with *arr instance
|
||||
*
|
||||
* **x-integration-notes:** This endpoint is used from the dashboard UI when an admin
|
||||
* clicks "Blocklist + Re-search" on a failed download. The arr instance credentials
|
||||
* are passed from the download object (which includes them for admin users).
|
||||
* **x-integration-notes:** This endpoint is used from the dashboard UI when a qualified user or admin
|
||||
* clicks "Blocklist + Re-search" on a stalled or failed download.
|
||||
* security:
|
||||
* - CookieAuth: []
|
||||
* - CsrfToken: []
|
||||
@@ -627,13 +629,13 @@ router.get('/stream', requireAuth, async (req, res) => {
|
||||
* example:
|
||||
* error: "Missing required fields"
|
||||
* '403':
|
||||
* description: Admin access required
|
||||
* description: Permission denied (admin or qualifying conditions required)
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/ErrorResponse'
|
||||
* example:
|
||||
* error: "Admin access required"
|
||||
* error: "Permission denied: admin or qualifying conditions required"
|
||||
* '502':
|
||||
* description: Failed to communicate with *arr instance
|
||||
* content:
|
||||
|
||||
@@ -142,6 +142,10 @@ describe('Swagger Coverage', () => {
|
||||
expect(paths['/api/webhook/sonarr'].post).toBeDefined();
|
||||
expect(paths['/api/webhook/radarr']).toBeDefined();
|
||||
expect(paths['/api/webhook/radarr'].post).toBeDefined();
|
||||
expect(paths['/api/webhook/ombi']).toBeDefined();
|
||||
expect(paths['/api/webhook/ombi'].post).toBeDefined();
|
||||
expect(paths['/api/webhook/config']).toBeDefined();
|
||||
expect(paths['/api/webhook/config'].get).toBeDefined();
|
||||
});
|
||||
|
||||
it('should have Sonarr proxy endpoints documented', () => {
|
||||
@@ -179,6 +183,19 @@ describe('Swagger Coverage', () => {
|
||||
expect(paths['/api/emby/session/{sessionId}/user']).toBeDefined();
|
||||
});
|
||||
|
||||
it('should have Ombi endpoints documented', () => {
|
||||
const paths = openapiSpec.paths;
|
||||
|
||||
expect(paths['/api/ombi/requests']).toBeDefined();
|
||||
expect(paths['/api/ombi/requests'].get).toBeDefined();
|
||||
expect(paths['/api/ombi/webhook/enable']).toBeDefined();
|
||||
expect(paths['/api/ombi/webhook/enable'].post).toBeDefined();
|
||||
expect(paths['/api/ombi/webhook/status']).toBeDefined();
|
||||
expect(paths['/api/ombi/webhook/status'].get).toBeDefined();
|
||||
expect(paths['/api/ombi/webhook/test']).toBeDefined();
|
||||
expect(paths['/api/ombi/webhook/test'].post).toBeDefined();
|
||||
});
|
||||
|
||||
it('should return 200 for Swagger UI endpoint', async () => {
|
||||
const response = await request(app).get('/api/swagger').redirects(1);
|
||||
expect(response.status).toBe(200);
|
||||
|
||||
Reference in New Issue
Block a user