diff options
Diffstat (limited to 'server')
-rw-r--r-- | server/controllers/api/runners/jobs.ts | 29 | ||||
-rw-r--r-- | server/helpers/custom-validators/runners/jobs.ts | 16 | ||||
-rw-r--r-- | server/lib/runners/runner.ts | 16 | ||||
-rw-r--r-- | server/middlewares/validators/runners/jobs.ts | 30 | ||||
-rw-r--r-- | server/models/runner/runner-job.ts | 26 | ||||
-rw-r--r-- | server/tests/api/check-params/runners.ts | 52 | ||||
-rw-r--r-- | server/tests/api/runners/runner-common.ts | 51 |
7 files changed, 192 insertions, 28 deletions
diff --git a/server/controllers/api/runners/jobs.ts b/server/controllers/api/runners/jobs.ts index be5911b53..e9e2ddf49 100644 --- a/server/controllers/api/runners/jobs.ts +++ b/server/controllers/api/runners/jobs.ts | |||
@@ -5,7 +5,7 @@ import { logger, loggerTagsFactory } from '@server/helpers/logger' | |||
5 | import { generateRunnerJobToken } from '@server/helpers/token-generator' | 5 | import { generateRunnerJobToken } from '@server/helpers/token-generator' |
6 | import { MIMETYPES } from '@server/initializers/constants' | 6 | import { MIMETYPES } from '@server/initializers/constants' |
7 | import { sequelizeTypescript } from '@server/initializers/database' | 7 | import { sequelizeTypescript } from '@server/initializers/database' |
8 | import { getRunnerJobHandlerClass, updateLastRunnerContact } from '@server/lib/runners' | 8 | import { getRunnerJobHandlerClass, runnerJobCanBeCancelled, updateLastRunnerContact } from '@server/lib/runners' |
9 | import { | 9 | import { |
10 | apiRateLimiter, | 10 | apiRateLimiter, |
11 | asyncMiddleware, | 11 | asyncMiddleware, |
@@ -23,6 +23,7 @@ import { | |||
23 | errorRunnerJobValidator, | 23 | errorRunnerJobValidator, |
24 | getRunnerFromTokenValidator, | 24 | getRunnerFromTokenValidator, |
25 | jobOfRunnerGetValidatorFactory, | 25 | jobOfRunnerGetValidatorFactory, |
26 | listRunnerJobsValidator, | ||
26 | runnerJobGetValidator, | 27 | runnerJobGetValidator, |
27 | successRunnerJobValidator, | 28 | successRunnerJobValidator, |
28 | updateRunnerJobValidator | 29 | updateRunnerJobValidator |
@@ -131,9 +132,17 @@ runnerJobsRouter.get('/jobs', | |||
131 | runnerJobsSortValidator, | 132 | runnerJobsSortValidator, |
132 | setDefaultSort, | 133 | setDefaultSort, |
133 | setDefaultPagination, | 134 | setDefaultPagination, |
135 | listRunnerJobsValidator, | ||
134 | asyncMiddleware(listRunnerJobs) | 136 | asyncMiddleware(listRunnerJobs) |
135 | ) | 137 | ) |
136 | 138 | ||
139 | runnerJobsRouter.delete('/jobs/:jobUUID', | ||
140 | authenticate, | ||
141 | ensureUserHasRight(UserRight.MANAGE_RUNNERS), | ||
142 | asyncMiddleware(runnerJobGetValidator), | ||
143 | asyncMiddleware(deleteRunnerJob) | ||
144 | ) | ||
145 | |||
137 | // --------------------------------------------------------------------------- | 146 | // --------------------------------------------------------------------------- |
138 | 147 | ||
139 | export { | 148 | export { |
@@ -374,6 +383,21 @@ async function cancelRunnerJob (req: express.Request, res: express.Response) { | |||
374 | return res.sendStatus(HttpStatusCode.NO_CONTENT_204) | 383 | return res.sendStatus(HttpStatusCode.NO_CONTENT_204) |
375 | } | 384 | } |
376 | 385 | ||
386 | async function deleteRunnerJob (req: express.Request, res: express.Response) { | ||
387 | const runnerJob = res.locals.runnerJob | ||
388 | |||
389 | logger.info('Deleting job %s (%s)', runnerJob.uuid, runnerJob.type, lTags(runnerJob.uuid, runnerJob.type)) | ||
390 | |||
391 | if (runnerJobCanBeCancelled(runnerJob)) { | ||
392 | const RunnerJobHandler = getRunnerJobHandlerClass(runnerJob) | ||
393 | await new RunnerJobHandler().cancel({ runnerJob }) | ||
394 | } | ||
395 | |||
396 | await runnerJob.destroy() | ||
397 | |||
398 | return res.sendStatus(HttpStatusCode.NO_CONTENT_204) | ||
399 | } | ||
400 | |||
377 | async function listRunnerJobs (req: express.Request, res: express.Response) { | 401 | async function listRunnerJobs (req: express.Request, res: express.Response) { |
378 | const query: ListRunnerJobsQuery = req.query | 402 | const query: ListRunnerJobsQuery = req.query |
379 | 403 | ||
@@ -381,7 +405,8 @@ async function listRunnerJobs (req: express.Request, res: express.Response) { | |||
381 | start: query.start, | 405 | start: query.start, |
382 | count: query.count, | 406 | count: query.count, |
383 | sort: query.sort, | 407 | sort: query.sort, |
384 | search: query.search | 408 | search: query.search, |
409 | stateOneOf: query.stateOneOf | ||
385 | }) | 410 | }) |
386 | 411 | ||
387 | return res.json({ | 412 | return res.json({ |
diff --git a/server/helpers/custom-validators/runners/jobs.ts b/server/helpers/custom-validators/runners/jobs.ts index 725a7658f..6349e79ba 100644 --- a/server/helpers/custom-validators/runners/jobs.ts +++ b/server/helpers/custom-validators/runners/jobs.ts | |||
@@ -1,6 +1,6 @@ | |||
1 | import { UploadFilesForCheck } from 'express' | 1 | import { UploadFilesForCheck } from 'express' |
2 | import validator from 'validator' | 2 | import validator from 'validator' |
3 | import { CONSTRAINTS_FIELDS } from '@server/initializers/constants' | 3 | import { CONSTRAINTS_FIELDS, RUNNER_JOB_STATES } from '@server/initializers/constants' |
4 | import { | 4 | import { |
5 | LiveRTMPHLSTranscodingSuccess, | 5 | LiveRTMPHLSTranscodingSuccess, |
6 | RunnerJobSuccessPayload, | 6 | RunnerJobSuccessPayload, |
@@ -11,7 +11,7 @@ import { | |||
11 | VODHLSTranscodingSuccess, | 11 | VODHLSTranscodingSuccess, |
12 | VODWebVideoTranscodingSuccess | 12 | VODWebVideoTranscodingSuccess |
13 | } from '@shared/models' | 13 | } from '@shared/models' |
14 | import { exists, isFileValid, isSafeFilename } from '../misc' | 14 | import { exists, isArray, isFileValid, isSafeFilename } from '../misc' |
15 | 15 | ||
16 | const RUNNER_JOBS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.RUNNER_JOBS | 16 | const RUNNER_JOBS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.RUNNER_JOBS |
17 | 17 | ||
@@ -56,6 +56,14 @@ function isRunnerJobErrorMessageValid (value: string) { | |||
56 | return validator.isLength(value, RUNNER_JOBS_CONSTRAINTS_FIELDS.ERROR_MESSAGE) | 56 | return validator.isLength(value, RUNNER_JOBS_CONSTRAINTS_FIELDS.ERROR_MESSAGE) |
57 | } | 57 | } |
58 | 58 | ||
59 | function isRunnerJobStateValid (value: any) { | ||
60 | return exists(value) && RUNNER_JOB_STATES[value] !== undefined | ||
61 | } | ||
62 | |||
63 | function isRunnerJobArrayOfStateValid (value: any) { | ||
64 | return isArray(value) && value.every(v => isRunnerJobStateValid(v)) | ||
65 | } | ||
66 | |||
59 | // --------------------------------------------------------------------------- | 67 | // --------------------------------------------------------------------------- |
60 | 68 | ||
61 | export { | 69 | export { |
@@ -65,7 +73,9 @@ export { | |||
65 | isRunnerJobTokenValid, | 73 | isRunnerJobTokenValid, |
66 | isRunnerJobErrorMessageValid, | 74 | isRunnerJobErrorMessageValid, |
67 | isRunnerJobProgressValid, | 75 | isRunnerJobProgressValid, |
68 | isRunnerJobAbortReasonValid | 76 | isRunnerJobAbortReasonValid, |
77 | isRunnerJobArrayOfStateValid, | ||
78 | isRunnerJobStateValid | ||
69 | } | 79 | } |
70 | 80 | ||
71 | // --------------------------------------------------------------------------- | 81 | // --------------------------------------------------------------------------- |
diff --git a/server/lib/runners/runner.ts b/server/lib/runners/runner.ts index 921cae6f2..947fdb3f0 100644 --- a/server/lib/runners/runner.ts +++ b/server/lib/runners/runner.ts | |||
@@ -2,8 +2,9 @@ import express from 'express' | |||
2 | import { retryTransactionWrapper } from '@server/helpers/database-utils' | 2 | import { retryTransactionWrapper } from '@server/helpers/database-utils' |
3 | import { logger, loggerTagsFactory } from '@server/helpers/logger' | 3 | import { logger, loggerTagsFactory } from '@server/helpers/logger' |
4 | import { sequelizeTypescript } from '@server/initializers/database' | 4 | import { sequelizeTypescript } from '@server/initializers/database' |
5 | import { MRunner } from '@server/types/models/runners' | 5 | import { MRunner, MRunnerJob } from '@server/types/models/runners' |
6 | import { RUNNER_JOBS } from '@server/initializers/constants' | 6 | import { RUNNER_JOBS } from '@server/initializers/constants' |
7 | import { RunnerJobState } from '@shared/models' | ||
7 | 8 | ||
8 | const lTags = loggerTagsFactory('runner') | 9 | const lTags = loggerTagsFactory('runner') |
9 | 10 | ||
@@ -32,6 +33,17 @@ function updateLastRunnerContact (req: express.Request, runner: MRunner) { | |||
32 | .finally(() => updatingRunner.delete(runner.id)) | 33 | .finally(() => updatingRunner.delete(runner.id)) |
33 | } | 34 | } |
34 | 35 | ||
36 | function runnerJobCanBeCancelled (runnerJob: MRunnerJob) { | ||
37 | const allowedStates = new Set<RunnerJobState>([ | ||
38 | RunnerJobState.PENDING, | ||
39 | RunnerJobState.PROCESSING, | ||
40 | RunnerJobState.WAITING_FOR_PARENT_JOB | ||
41 | ]) | ||
42 | |||
43 | return allowedStates.has(runnerJob.state) | ||
44 | } | ||
45 | |||
35 | export { | 46 | export { |
36 | updateLastRunnerContact | 47 | updateLastRunnerContact, |
48 | runnerJobCanBeCancelled | ||
37 | } | 49 | } |
diff --git a/server/middlewares/validators/runners/jobs.ts b/server/middlewares/validators/runners/jobs.ts index 384b209ba..62f9340a5 100644 --- a/server/middlewares/validators/runners/jobs.ts +++ b/server/middlewares/validators/runners/jobs.ts | |||
@@ -1,8 +1,9 @@ | |||
1 | import express from 'express' | 1 | import express from 'express' |
2 | import { body, param } from 'express-validator' | 2 | import { body, param, query } from 'express-validator' |
3 | import { isUUIDValid } from '@server/helpers/custom-validators/misc' | 3 | import { exists, isUUIDValid } from '@server/helpers/custom-validators/misc' |
4 | import { | 4 | import { |
5 | isRunnerJobAbortReasonValid, | 5 | isRunnerJobAbortReasonValid, |
6 | isRunnerJobArrayOfStateValid, | ||
6 | isRunnerJobErrorMessageValid, | 7 | isRunnerJobErrorMessageValid, |
7 | isRunnerJobProgressValid, | 8 | isRunnerJobProgressValid, |
8 | isRunnerJobSuccessPayloadValid, | 9 | isRunnerJobSuccessPayloadValid, |
@@ -12,7 +13,9 @@ import { | |||
12 | import { isRunnerTokenValid } from '@server/helpers/custom-validators/runners/runners' | 13 | import { isRunnerTokenValid } from '@server/helpers/custom-validators/runners/runners' |
13 | import { cleanUpReqFiles } from '@server/helpers/express-utils' | 14 | import { cleanUpReqFiles } from '@server/helpers/express-utils' |
14 | import { LiveManager } from '@server/lib/live' | 15 | import { LiveManager } from '@server/lib/live' |
16 | import { runnerJobCanBeCancelled } from '@server/lib/runners' | ||
15 | import { RunnerJobModel } from '@server/models/runner/runner-job' | 17 | import { RunnerJobModel } from '@server/models/runner/runner-job' |
18 | import { arrayify } from '@shared/core-utils' | ||
16 | import { | 19 | import { |
17 | HttpStatusCode, | 20 | HttpStatusCode, |
18 | RunnerJobLiveRTMPHLSTranscodingPrivatePayload, | 21 | RunnerJobLiveRTMPHLSTranscodingPrivatePayload, |
@@ -119,13 +122,7 @@ export const cancelRunnerJobValidator = [ | |||
119 | (req: express.Request, res: express.Response, next: express.NextFunction) => { | 122 | (req: express.Request, res: express.Response, next: express.NextFunction) => { |
120 | const runnerJob = res.locals.runnerJob | 123 | const runnerJob = res.locals.runnerJob |
121 | 124 | ||
122 | const allowedStates = new Set<RunnerJobState>([ | 125 | if (runnerJobCanBeCancelled(runnerJob) !== true) { |
123 | RunnerJobState.PENDING, | ||
124 | RunnerJobState.PROCESSING, | ||
125 | RunnerJobState.WAITING_FOR_PARENT_JOB | ||
126 | ]) | ||
127 | |||
128 | if (allowedStates.has(runnerJob.state) !== true) { | ||
129 | return res.fail({ | 126 | return res.fail({ |
130 | status: HttpStatusCode.BAD_REQUEST_400, | 127 | status: HttpStatusCode.BAD_REQUEST_400, |
131 | message: 'Cannot cancel this job that is not in "pending", "processing" or "waiting for parent job" state', | 128 | message: 'Cannot cancel this job that is not in "pending", "processing" or "waiting for parent job" state', |
@@ -137,6 +134,21 @@ export const cancelRunnerJobValidator = [ | |||
137 | } | 134 | } |
138 | ] | 135 | ] |
139 | 136 | ||
137 | export const listRunnerJobsValidator = [ | ||
138 | query('search') | ||
139 | .optional() | ||
140 | .custom(exists), | ||
141 | |||
142 | query('stateOneOf') | ||
143 | .optional() | ||
144 | .customSanitizer(arrayify) | ||
145 | .custom(isRunnerJobArrayOfStateValid), | ||
146 | |||
147 | (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
148 | return next() | ||
149 | } | ||
150 | ] | ||
151 | |||
140 | export const runnerJobGetValidator = [ | 152 | export const runnerJobGetValidator = [ |
141 | param('jobUUID').custom(isUUIDValid), | 153 | param('jobUUID').custom(isUUIDValid), |
142 | 154 | ||
diff --git a/server/models/runner/runner-job.ts b/server/models/runner/runner-job.ts index add6f9a43..f2ffd6a84 100644 --- a/server/models/runner/runner-job.ts +++ b/server/models/runner/runner-job.ts | |||
@@ -1,4 +1,4 @@ | |||
1 | import { FindOptions, Op, Transaction } from 'sequelize' | 1 | import { Op, Transaction } from 'sequelize' |
2 | import { | 2 | import { |
3 | AllowNull, | 3 | AllowNull, |
4 | BelongsTo, | 4 | BelongsTo, |
@@ -13,7 +13,7 @@ import { | |||
13 | Table, | 13 | Table, |
14 | UpdatedAt | 14 | UpdatedAt |
15 | } from 'sequelize-typescript' | 15 | } from 'sequelize-typescript' |
16 | import { isUUIDValid } from '@server/helpers/custom-validators/misc' | 16 | import { isArray, isUUIDValid } from '@server/helpers/custom-validators/misc' |
17 | import { CONSTRAINTS_FIELDS, RUNNER_JOB_STATES } from '@server/initializers/constants' | 17 | import { CONSTRAINTS_FIELDS, RUNNER_JOB_STATES } from '@server/initializers/constants' |
18 | import { MRunnerJob, MRunnerJobRunner, MRunnerJobRunnerParent } from '@server/types/models/runners' | 18 | import { MRunnerJob, MRunnerJobRunner, MRunnerJobRunnerParent } from '@server/types/models/runners' |
19 | import { RunnerJob, RunnerJobAdmin, RunnerJobPayload, RunnerJobPrivatePayload, RunnerJobState, RunnerJobType } from '@shared/models' | 19 | import { RunnerJob, RunnerJobAdmin, RunnerJobPayload, RunnerJobPrivatePayload, RunnerJobState, RunnerJobType } from '@shared/models' |
@@ -227,28 +227,38 @@ export class RunnerJobModel extends Model<Partial<AttributesOnly<RunnerJobModel> | |||
227 | count: number | 227 | count: number |
228 | sort: string | 228 | sort: string |
229 | search?: string | 229 | search?: string |
230 | stateOneOf?: RunnerJobState[] | ||
230 | }) { | 231 | }) { |
231 | const { start, count, sort, search } = options | 232 | const { start, count, sort, search, stateOneOf } = options |
232 | 233 | ||
233 | const query: FindOptions = { | 234 | const query = { |
234 | offset: start, | 235 | offset: start, |
235 | limit: count, | 236 | limit: count, |
236 | order: getSort(sort) | 237 | order: getSort(sort), |
238 | where: [] | ||
237 | } | 239 | } |
238 | 240 | ||
239 | if (search) { | 241 | if (search) { |
240 | if (isUUIDValid(search)) { | 242 | if (isUUIDValid(search)) { |
241 | query.where = { uuid: search } | 243 | query.where.push({ uuid: search }) |
242 | } else { | 244 | } else { |
243 | query.where = { | 245 | query.where.push({ |
244 | [Op.or]: [ | 246 | [Op.or]: [ |
245 | searchAttribute(search, 'type'), | 247 | searchAttribute(search, 'type'), |
246 | searchAttribute(search, '$Runner.name$') | 248 | searchAttribute(search, '$Runner.name$') |
247 | ] | 249 | ] |
248 | } | 250 | }) |
249 | } | 251 | } |
250 | } | 252 | } |
251 | 253 | ||
254 | if (isArray(stateOneOf) && stateOneOf.length !== 0) { | ||
255 | query.where.push({ | ||
256 | state: { | ||
257 | [Op.in]: stateOneOf | ||
258 | } | ||
259 | }) | ||
260 | } | ||
261 | |||
252 | return Promise.all([ | 262 | return Promise.all([ |
253 | RunnerJobModel.scope([ ScopeNames.WITH_RUNNER ]).count(query), | 263 | RunnerJobModel.scope([ ScopeNames.WITH_RUNNER ]).count(query), |
254 | RunnerJobModel.scope([ ScopeNames.WITH_RUNNER, ScopeNames.WITH_PARENT ]).findAll<MRunnerJobRunnerParent>(query) | 264 | RunnerJobModel.scope([ ScopeNames.WITH_RUNNER, ScopeNames.WITH_PARENT ]).findAll<MRunnerJobRunnerParent>(query) |
diff --git a/server/tests/api/check-params/runners.ts b/server/tests/api/check-params/runners.ts index 9112ff716..7f9a0cd32 100644 --- a/server/tests/api/check-params/runners.ts +++ b/server/tests/api/check-params/runners.ts | |||
@@ -1,14 +1,14 @@ | |||
1 | import { basename } from 'path' | ||
2 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ | 1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ |
2 | import { basename } from 'path' | ||
3 | import { checkBadCountPagination, checkBadSortPagination, checkBadStartPagination } from '@server/tests/shared' | 3 | import { checkBadCountPagination, checkBadSortPagination, checkBadStartPagination } from '@server/tests/shared' |
4 | import { | 4 | import { |
5 | HttpStatusCode, | 5 | HttpStatusCode, |
6 | isVideoStudioTaskIntro, | 6 | isVideoStudioTaskIntro, |
7 | RunnerJob, | 7 | RunnerJob, |
8 | RunnerJobState, | 8 | RunnerJobState, |
9 | RunnerJobStudioTranscodingPayload, | ||
9 | RunnerJobSuccessPayload, | 10 | RunnerJobSuccessPayload, |
10 | RunnerJobUpdatePayload, | 11 | RunnerJobUpdatePayload, |
11 | RunnerJobStudioTranscodingPayload, | ||
12 | VideoPrivacy, | 12 | VideoPrivacy, |
13 | VideoStudioTaskIntro | 13 | VideoStudioTaskIntro |
14 | } from '@shared/models' | 14 | } from '@shared/models' |
@@ -236,6 +236,10 @@ describe('Test managing runners', function () { | |||
236 | await checkBadSortPagination(server.url, path, server.accessToken) | 236 | await checkBadSortPagination(server.url, path, server.accessToken) |
237 | }) | 237 | }) |
238 | 238 | ||
239 | it('Should fail with an invalid state', async function () { | ||
240 | await server.runners.list({ start: 0, count: 5, sort: '-createdAt' }) | ||
241 | }) | ||
242 | |||
239 | it('Should succeed to list with the correct params', async function () { | 243 | it('Should succeed to list with the correct params', async function () { |
240 | await server.runners.list({ start: 0, count: 5, sort: '-createdAt' }) | 244 | await server.runners.list({ start: 0, count: 5, sort: '-createdAt' }) |
241 | }) | 245 | }) |
@@ -307,8 +311,48 @@ describe('Test managing runners', function () { | |||
307 | await checkBadSortPagination(server.url, path, server.accessToken) | 311 | await checkBadSortPagination(server.url, path, server.accessToken) |
308 | }) | 312 | }) |
309 | 313 | ||
310 | it('Should succeed to list with the correct params', async function () { | 314 | it('Should fail with an invalid state', async function () { |
311 | await server.runnerJobs.list({ start: 0, count: 5, sort: '-createdAt' }) | 315 | await server.runnerJobs.list({ start: 0, count: 5, sort: '-createdAt', stateOneOf: 42 as any }) |
316 | await server.runnerJobs.list({ start: 0, count: 5, sort: '-createdAt', stateOneOf: [ 42 ] as any }) | ||
317 | }) | ||
318 | |||
319 | it('Should succeed with the correct params', async function () { | ||
320 | await server.runnerJobs.list({ start: 0, count: 5, sort: '-createdAt', stateOneOf: [ RunnerJobState.COMPLETED ] }) | ||
321 | }) | ||
322 | }) | ||
323 | |||
324 | describe('Delete', function () { | ||
325 | let jobUUID: string | ||
326 | |||
327 | before(async function () { | ||
328 | this.timeout(60000) | ||
329 | |||
330 | await server.videos.quickUpload({ name: 'video' }) | ||
331 | await waitJobs([ server ]) | ||
332 | |||
333 | const { availableJobs } = await server.runnerJobs.request({ runnerToken }) | ||
334 | jobUUID = availableJobs[0].uuid | ||
335 | }) | ||
336 | |||
337 | it('Should fail without oauth token', async function () { | ||
338 | await server.runnerJobs.deleteByAdmin({ token: null, jobUUID, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 }) | ||
339 | }) | ||
340 | |||
341 | it('Should fail without admin rights', async function () { | ||
342 | await server.runnerJobs.deleteByAdmin({ token: userToken, jobUUID, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) | ||
343 | }) | ||
344 | |||
345 | it('Should fail with a bad job uuid', async function () { | ||
346 | await server.runnerJobs.deleteByAdmin({ jobUUID: 'hello', expectedStatus: HttpStatusCode.BAD_REQUEST_400 }) | ||
347 | }) | ||
348 | |||
349 | it('Should fail with an unknown job uuid', async function () { | ||
350 | const jobUUID = badUUID | ||
351 | await server.runnerJobs.deleteByAdmin({ jobUUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 }) | ||
352 | }) | ||
353 | |||
354 | it('Should succeed with the correct params', async function () { | ||
355 | await server.runnerJobs.deleteByAdmin({ jobUUID }) | ||
312 | }) | 356 | }) |
313 | }) | 357 | }) |
314 | 358 | ||
diff --git a/server/tests/api/runners/runner-common.ts b/server/tests/api/runners/runner-common.ts index 34a51abe7..7fed75f40 100644 --- a/server/tests/api/runners/runner-common.ts +++ b/server/tests/api/runners/runner-common.ts | |||
@@ -339,6 +339,30 @@ describe('Test runner common actions', function () { | |||
339 | 339 | ||
340 | expect(data).to.not.have.lengthOf(0) | 340 | expect(data).to.not.have.lengthOf(0) |
341 | expect(total).to.not.equal(0) | 341 | expect(total).to.not.equal(0) |
342 | |||
343 | for (const job of data) { | ||
344 | expect(job.type).to.include('hls') | ||
345 | } | ||
346 | } | ||
347 | }) | ||
348 | |||
349 | it('Should filter jobs', async function () { | ||
350 | { | ||
351 | const { total, data } = await server.runnerJobs.list({ stateOneOf: [ RunnerJobState.WAITING_FOR_PARENT_JOB ] }) | ||
352 | |||
353 | expect(data).to.not.have.lengthOf(0) | ||
354 | expect(total).to.not.equal(0) | ||
355 | |||
356 | for (const job of data) { | ||
357 | expect(job.state.label).to.equal('Waiting for parent job to finish') | ||
358 | } | ||
359 | } | ||
360 | |||
361 | { | ||
362 | const { total, data } = await server.runnerJobs.list({ stateOneOf: [ RunnerJobState.COMPLETED ] }) | ||
363 | |||
364 | expect(data).to.have.lengthOf(0) | ||
365 | expect(total).to.equal(0) | ||
342 | } | 366 | } |
343 | }) | 367 | }) |
344 | }) | 368 | }) |
@@ -598,6 +622,33 @@ describe('Test runner common actions', function () { | |||
598 | }) | 622 | }) |
599 | }) | 623 | }) |
600 | 624 | ||
625 | describe('Remove', function () { | ||
626 | |||
627 | it('Should remove a pending job', async function () { | ||
628 | await server.videos.quickUpload({ name: 'video' }) | ||
629 | await waitJobs([ server ]) | ||
630 | |||
631 | { | ||
632 | const { data } = await server.runnerJobs.list({ count: 10, sort: '-updatedAt' }) | ||
633 | |||
634 | const pendingJob = data.find(j => j.state.id === RunnerJobState.PENDING) | ||
635 | jobUUID = pendingJob.uuid | ||
636 | |||
637 | await server.runnerJobs.deleteByAdmin({ jobUUID }) | ||
638 | } | ||
639 | |||
640 | { | ||
641 | const { data } = await server.runnerJobs.list({ count: 10, sort: '-updatedAt' }) | ||
642 | |||
643 | const parent = data.find(j => j.uuid === jobUUID) | ||
644 | expect(parent).to.not.exist | ||
645 | |||
646 | const children = data.filter(j => j.parent?.uuid === jobUUID) | ||
647 | expect(children).to.have.lengthOf(0) | ||
648 | } | ||
649 | }) | ||
650 | }) | ||
651 | |||
601 | describe('Stalled jobs', function () { | 652 | describe('Stalled jobs', function () { |
602 | 653 | ||
603 | it('Should abort stalled jobs', async function () { | 654 | it('Should abort stalled jobs', async function () { |