]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/tests/api/check-params/runners.ts
Improve remote runner config UX
[github/Chocobozzz/PeerTube.git] / server / tests / api / check-params / runners.ts
CommitLineData
5e47f6ab 1import { basename } from 'path'
d102de1b
C
2/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
3import { checkBadCountPagination, checkBadSortPagination, checkBadStartPagination } from '@server/tests/shared'
5e47f6ab
C
4import {
5 HttpStatusCode,
6 isVideoStudioTaskIntro,
7 RunnerJob,
8 RunnerJobState,
9 RunnerJobSuccessPayload,
10 RunnerJobUpdatePayload,
ab14f0e0 11 RunnerJobStudioTranscodingPayload,
5e47f6ab
C
12 VideoPrivacy,
13 VideoStudioTaskIntro
14} from '@shared/models'
d102de1b
C
15import {
16 cleanupTests,
17 createSingleServer,
18 makePostBodyRequest,
19 PeerTubeServer,
20 sendRTMPStream,
21 setAccessTokensToServers,
22 setDefaultVideoChannel,
23 stopFfmpeg,
5e47f6ab 24 VideoStudioCommand,
d102de1b
C
25 waitJobs
26} from '@shared/server-commands'
27
28const badUUID = '910ec12a-d9e6-458b-a274-0abb655f9464'
29
30describe('Test managing runners', function () {
31 let server: PeerTubeServer
32
33 let userToken: string
34
35 let registrationTokenId: number
36 let registrationToken: string
37
38 let runnerToken: string
39 let runnerToken2: string
40
41 let completedJobToken: string
42 let completedJobUUID: string
43
44 let cancelledJobUUID: string
45
46 before(async function () {
47 this.timeout(120000)
48
49 const config = {
50 rates_limit: {
51 api: {
52 max: 5000
53 }
54 }
55 }
56
57 server = await createSingleServer(1, config)
58 await setAccessTokensToServers([ server ])
59 await setDefaultVideoChannel([ server ])
60
61 userToken = await server.users.generateUserAndToken('user1')
62
63 const { data } = await server.runnerRegistrationTokens.list()
64 registrationToken = data[0].registrationToken
65 registrationTokenId = data[0].id
66
67 await server.config.enableTranscoding(true, true)
5e47f6ab 68 await server.config.enableStudio()
d102de1b 69 await server.config.enableRemoteTranscoding()
5e47f6ab
C
70 await server.config.enableRemoteStudio()
71
d102de1b
C
72 runnerToken = await server.runners.autoRegisterRunner()
73 runnerToken2 = await server.runners.autoRegisterRunner()
74
75 {
76 await server.videos.quickUpload({ name: 'video 1' })
77 await server.videos.quickUpload({ name: 'video 2' })
78
79 await waitJobs([ server ])
80
81 {
82 const job = await server.runnerJobs.autoProcessWebVideoJob(runnerToken)
83 completedJobToken = job.jobToken
84 completedJobUUID = job.uuid
85 }
86
87 {
88 const { job } = await server.runnerJobs.autoAccept({ runnerToken })
89 cancelledJobUUID = job.uuid
90 await server.runnerJobs.cancelByAdmin({ jobUUID: cancelledJobUUID })
91 }
92 }
93 })
94
95 describe('Managing runner registration tokens', function () {
96
97 describe('Common', function () {
98
99 it('Should fail to generate, list or delete runner registration token without oauth token', async function () {
100 const expectedStatus = HttpStatusCode.UNAUTHORIZED_401
101
102 await server.runnerRegistrationTokens.generate({ token: null, expectedStatus })
103 await server.runnerRegistrationTokens.list({ token: null, expectedStatus })
104 await server.runnerRegistrationTokens.delete({ token: null, id: registrationTokenId, expectedStatus })
105 })
106
107 it('Should fail to generate, list or delete runner registration token without admin rights', async function () {
108 const expectedStatus = HttpStatusCode.FORBIDDEN_403
109
110 await server.runnerRegistrationTokens.generate({ token: userToken, expectedStatus })
111 await server.runnerRegistrationTokens.list({ token: userToken, expectedStatus })
112 await server.runnerRegistrationTokens.delete({ token: userToken, id: registrationTokenId, expectedStatus })
113 })
114 })
115
116 describe('Delete', function () {
117
118 it('Should fail to delete with a bad id', async function () {
119 await server.runnerRegistrationTokens.delete({ id: 404, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
120 })
121 })
122
123 describe('List', function () {
124 const path = '/api/v1/runners/registration-tokens'
125
126 it('Should fail to list with a bad start pagination', async function () {
127 await checkBadStartPagination(server.url, path, server.accessToken)
128 })
129
130 it('Should fail to list with a bad count pagination', async function () {
131 await checkBadCountPagination(server.url, path, server.accessToken)
132 })
133
134 it('Should fail to list with an incorrect sort', async function () {
135 await checkBadSortPagination(server.url, path, server.accessToken)
136 })
137
138 it('Should succeed to list with the correct params', async function () {
139 await server.runnerRegistrationTokens.list({ start: 0, count: 5, sort: '-createdAt' })
140 })
141 })
142 })
143
144 describe('Managing runners', function () {
145 let toDeleteId: number
146
147 describe('Register', function () {
148 const name = 'runner name'
149
150 it('Should fail with a bad registration token', async function () {
151 const expectedStatus = HttpStatusCode.BAD_REQUEST_400
152
153 await server.runners.register({ name, registrationToken: 'a'.repeat(4000), expectedStatus })
154 await server.runners.register({ name, registrationToken: null, expectedStatus })
155 })
156
157 it('Should fail with an unknown registration token', async function () {
158 await server.runners.register({ name, registrationToken: 'aaa', expectedStatus: HttpStatusCode.NOT_FOUND_404 })
159 })
160
161 it('Should fail with a bad name', async function () {
162 const expectedStatus = HttpStatusCode.BAD_REQUEST_400
163
164 await server.runners.register({ name: '', registrationToken, expectedStatus })
165 await server.runners.register({ name: 'a'.repeat(200), registrationToken, expectedStatus })
166 })
167
168 it('Should fail with an invalid description', async function () {
169 const expectedStatus = HttpStatusCode.BAD_REQUEST_400
170
171 await server.runners.register({ name, description: '', registrationToken, expectedStatus })
172 await server.runners.register({ name, description: 'a'.repeat(5000), registrationToken, expectedStatus })
173 })
174
175 it('Should succeed with the correct params', async function () {
176 const { id } = await server.runners.register({ name, description: 'super description', registrationToken })
177
178 toDeleteId = id
179 })
180 })
181
182 describe('Delete', function () {
183
184 it('Should fail without oauth token', async function () {
185 await server.runners.delete({ token: null, id: toDeleteId, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
186 })
187
188 it('Should fail without admin rights', async function () {
189 await server.runners.delete({ token: userToken, id: toDeleteId, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
190 })
191
192 it('Should fail with a bad id', async function () {
193 await server.runners.delete({ id: 'hi' as any, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
194 })
195
196 it('Should fail with an unknown id', async function () {
197 await server.runners.delete({ id: 404, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
198 })
199
200 it('Should succeed with the correct params', async function () {
201 await server.runners.delete({ id: toDeleteId })
202 })
203 })
204
205 describe('List', function () {
206 const path = '/api/v1/runners'
207
208 it('Should fail without oauth token', async function () {
209 await server.runners.list({ token: null, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
210 })
211
212 it('Should fail without admin rights', async function () {
213 await server.runners.list({ token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
214 })
215
216 it('Should fail to list with a bad start pagination', async function () {
217 await checkBadStartPagination(server.url, path, server.accessToken)
218 })
219
220 it('Should fail to list with a bad count pagination', async function () {
221 await checkBadCountPagination(server.url, path, server.accessToken)
222 })
223
224 it('Should fail to list with an incorrect sort', async function () {
225 await checkBadSortPagination(server.url, path, server.accessToken)
226 })
227
228 it('Should succeed to list with the correct params', async function () {
229 await server.runners.list({ start: 0, count: 5, sort: '-createdAt' })
230 })
231 })
232
233 })
234
235 describe('Runner jobs by admin', function () {
236
237 describe('Cancel', function () {
238 let jobUUID: string
239
240 before(async function () {
241 this.timeout(60000)
242
243 await server.videos.quickUpload({ name: 'video' })
244 await waitJobs([ server ])
245
246 const { availableJobs } = await server.runnerJobs.request({ runnerToken })
247 jobUUID = availableJobs[0].uuid
248 })
249
250 it('Should fail without oauth token', async function () {
251 await server.runnerJobs.cancelByAdmin({ token: null, jobUUID, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
252 })
253
254 it('Should fail without admin rights', async function () {
255 await server.runnerJobs.cancelByAdmin({ token: userToken, jobUUID, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
256 })
257
258 it('Should fail with a bad job uuid', async function () {
259 await server.runnerJobs.cancelByAdmin({ jobUUID: 'hello', expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
260 })
261
262 it('Should fail with an unknown job uuid', async function () {
263 const jobUUID = badUUID
264 await server.runnerJobs.cancelByAdmin({ jobUUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
265 })
266
5e47f6ab
C
267 it('Should fail with an already cancelled job', async function () {
268 await server.runnerJobs.cancelByAdmin({ jobUUID: cancelledJobUUID, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
269 })
270
d102de1b
C
271 it('Should succeed with the correct params', async function () {
272 await server.runnerJobs.cancelByAdmin({ jobUUID })
273 })
274 })
275
276 describe('List', function () {
277 const path = '/api/v1/runners/jobs'
278
279 it('Should fail without oauth token', async function () {
280 await server.runnerJobs.list({ token: null, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
281 })
282
283 it('Should fail without admin rights', async function () {
284 await server.runnerJobs.list({ token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
285 })
286
287 it('Should fail to list with a bad start pagination', async function () {
288 await checkBadStartPagination(server.url, path, server.accessToken)
289 })
290
291 it('Should fail to list with a bad count pagination', async function () {
292 await checkBadCountPagination(server.url, path, server.accessToken)
293 })
294
295 it('Should fail to list with an incorrect sort', async function () {
296 await checkBadSortPagination(server.url, path, server.accessToken)
297 })
298
299 it('Should succeed to list with the correct params', async function () {
300 await server.runnerJobs.list({ start: 0, count: 5, sort: '-createdAt' })
301 })
302 })
303
304 })
305
306 describe('Runner jobs by runners', function () {
307 let jobUUID: string
308 let jobToken: string
309 let videoUUID: string
310
311 let jobUUID2: string
312 let jobToken2: string
313
314 let videoUUID2: string
315
316 let pendingUUID: string
317
5e47f6ab
C
318 let videoStudioUUID: string
319 let studioFile: string
320
d102de1b 321 let liveAcceptedJob: RunnerJob & { jobToken: string }
5e47f6ab 322 let studioAcceptedJob: RunnerJob & { jobToken: string }
d102de1b 323
5e47f6ab 324 async function fetchVideoInputFiles (options: {
d102de1b
C
325 jobUUID: string
326 videoUUID: string
327 runnerToken: string
328 jobToken: string
329 expectedStatus: HttpStatusCode
330 }) {
331 const { jobUUID, expectedStatus, videoUUID, runnerToken, jobToken } = options
332
333 const basePath = '/api/v1/runners/jobs/' + jobUUID + '/files/videos/' + videoUUID
334 const paths = [ `${basePath}/max-quality`, `${basePath}/previews/max-quality` ]
335
336 for (const path of paths) {
337 await makePostBodyRequest({ url: server.url, path, fields: { runnerToken, jobToken }, expectedStatus })
338 }
339 }
340
5e47f6ab
C
341 async function fetchStudioFiles (options: {
342 jobUUID: string
343 videoUUID: string
344 runnerToken: string
345 jobToken: string
346 studioFile?: string
347 expectedStatus: HttpStatusCode
348 }) {
349 const { jobUUID, expectedStatus, videoUUID, runnerToken, jobToken, studioFile } = options
350
351 const path = `/api/v1/runners/jobs/${jobUUID}/files/videos/${videoUUID}/studio/task-files/${studioFile}`
352
353 await makePostBodyRequest({ url: server.url, path, fields: { runnerToken, jobToken }, expectedStatus })
354 }
355
d102de1b
C
356 before(async function () {
357 this.timeout(120000)
358
359 {
360 await server.runnerJobs.cancelAllJobs({ state: RunnerJobState.PENDING })
361 }
362
363 {
364 const { uuid } = await server.videos.quickUpload({ name: 'video' })
365 videoUUID = uuid
366
367 await waitJobs([ server ])
368
369 const { job } = await server.runnerJobs.autoAccept({ runnerToken })
370 jobUUID = job.uuid
371 jobToken = job.jobToken
372 }
373
374 {
375 const { uuid } = await server.videos.quickUpload({ name: 'video' })
376 videoUUID2 = uuid
377
378 await waitJobs([ server ])
379
380 const { job } = await server.runnerJobs.autoAccept({ runnerToken: runnerToken2 })
381 jobUUID2 = job.uuid
382 jobToken2 = job.jobToken
383 }
384
385 {
386 await server.videos.quickUpload({ name: 'video' })
387 await waitJobs([ server ])
388
389 const { availableJobs } = await server.runnerJobs.request({ runnerToken })
390 pendingUUID = availableJobs[0].uuid
391 }
392
5e47f6ab
C
393 {
394 await server.config.disableTranscoding()
395
396 const { uuid } = await server.videos.quickUpload({ name: 'video studio' })
397 videoStudioUUID = uuid
398
399 await server.config.enableTranscoding(true, true)
400 await server.config.enableStudio()
401
402 await server.videoStudio.createEditionTasks({
403 videoId: videoStudioUUID,
404 tasks: VideoStudioCommand.getComplexTask()
405 })
406
ab14f0e0 407 const { job } = await server.runnerJobs.autoAccept({ runnerToken, type: 'video-studio-transcoding' })
5e47f6ab
C
408 studioAcceptedJob = job
409
ab14f0e0 410 const tasks = (job.payload as RunnerJobStudioTranscodingPayload).tasks
5e47f6ab
C
411 const fileUrl = (tasks.find(t => isVideoStudioTaskIntro(t)) as VideoStudioTaskIntro).options.file as string
412 studioFile = basename(fileUrl)
413 }
414
d102de1b
C
415 {
416 await server.config.enableLive({
417 allowReplay: false,
418 resolutions: 'max',
419 transcoding: true
420 })
421
422 const { live } = await server.live.quickCreate({ permanentLive: true, saveReplay: false, privacy: VideoPrivacy.PUBLIC })
423
424 const ffmpegCommand = sendRTMPStream({ rtmpBaseUrl: live.rtmpUrl, streamKey: live.streamKey })
425 await waitJobs([ server ])
426
427 await server.runnerJobs.requestLiveJob(runnerToken)
428
429 const { job } = await server.runnerJobs.autoAccept({ runnerToken, type: 'live-rtmp-hls-transcoding' })
430 liveAcceptedJob = job
431
432 await stopFfmpeg(ffmpegCommand)
433 }
434 })
435
436 describe('Common runner tokens validations', function () {
437
438 async function testEndpoints (options: {
439 jobUUID: string
440 runnerToken: string
441 jobToken: string
442 expectedStatus: HttpStatusCode
443 }) {
d102de1b
C
444 await server.runnerJobs.abort({ ...options, reason: 'reason' })
445 await server.runnerJobs.update({ ...options })
446 await server.runnerJobs.error({ ...options, message: 'message' })
447 await server.runnerJobs.success({ ...options, payload: { videoFile: 'video_short.mp4' } })
448 }
449
450 it('Should fail with an invalid job uuid', async function () {
5e47f6ab
C
451 const options = { jobUUID: 'a', runnerToken, expectedStatus: HttpStatusCode.BAD_REQUEST_400 }
452
453 await testEndpoints({ ...options, jobToken })
454 await fetchVideoInputFiles({ ...options, videoUUID, jobToken })
455 await fetchStudioFiles({ ...options, videoUUID, jobToken: studioAcceptedJob.jobToken, studioFile })
d102de1b
C
456 })
457
458 it('Should fail with an unknown job uuid', async function () {
5e47f6ab
C
459 const options = { jobUUID: badUUID, runnerToken, expectedStatus: HttpStatusCode.NOT_FOUND_404 }
460
461 await testEndpoints({ ...options, jobToken })
462 await fetchVideoInputFiles({ ...options, videoUUID, jobToken })
463 await fetchStudioFiles({ ...options, jobToken: studioAcceptedJob.jobToken, videoUUID, studioFile })
d102de1b
C
464 })
465
466 it('Should fail with an invalid runner token', async function () {
5e47f6ab
C
467 const options = { runnerToken: '', expectedStatus: HttpStatusCode.BAD_REQUEST_400 }
468
469 await testEndpoints({ ...options, jobUUID, jobToken })
470 await fetchVideoInputFiles({ ...options, jobUUID, videoUUID, jobToken })
471 await fetchStudioFiles({
472 ...options,
473 jobToken: studioAcceptedJob.jobToken,
474 jobUUID: studioAcceptedJob.uuid,
475 videoUUID: videoStudioUUID,
476 studioFile
477 })
d102de1b
C
478 })
479
480 it('Should fail with an unknown runner token', async function () {
5e47f6ab
C
481 const options = { runnerToken: badUUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 }
482
483 await testEndpoints({ ...options, jobUUID, jobToken })
484 await fetchVideoInputFiles({ ...options, jobUUID, videoUUID, jobToken })
485 await fetchStudioFiles({
486 ...options,
487 jobToken: studioAcceptedJob.jobToken,
488 jobUUID: studioAcceptedJob.uuid,
489 videoUUID: videoStudioUUID,
490 studioFile
491 })
d102de1b
C
492 })
493
494 it('Should fail with an invalid job token job uuid', async function () {
5e47f6ab
C
495 const options = { runnerToken, jobToken: '', expectedStatus: HttpStatusCode.BAD_REQUEST_400 }
496
497 await testEndpoints({ ...options, jobUUID })
498 await fetchVideoInputFiles({ ...options, jobUUID, videoUUID })
499 await fetchStudioFiles({ ...options, jobUUID: studioAcceptedJob.uuid, videoUUID: videoStudioUUID, studioFile })
d102de1b
C
500 })
501
502 it('Should fail with an unknown job token job uuid', async function () {
5e47f6ab
C
503 const options = { runnerToken, jobToken: badUUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 }
504
505 await testEndpoints({ ...options, jobUUID })
506 await fetchVideoInputFiles({ ...options, jobUUID, videoUUID })
507 await fetchStudioFiles({ ...options, jobUUID: studioAcceptedJob.uuid, videoUUID: videoStudioUUID, studioFile })
d102de1b
C
508 })
509
510 it('Should fail with a runner token not associated to this job', async function () {
5e47f6ab
C
511 const options = { runnerToken: runnerToken2, expectedStatus: HttpStatusCode.NOT_FOUND_404 }
512
513 await testEndpoints({ ...options, jobUUID, jobToken })
514 await fetchVideoInputFiles({ ...options, jobUUID, videoUUID, jobToken })
515 await fetchStudioFiles({
516 ...options,
517 jobToken: studioAcceptedJob.jobToken,
518 jobUUID: studioAcceptedJob.uuid,
519 videoUUID: videoStudioUUID,
520 studioFile
521 })
d102de1b
C
522 })
523
524 it('Should fail with a job uuid not associated to the job token', async function () {
5e47f6ab
C
525 {
526 const options = { jobUUID: jobUUID2, runnerToken, expectedStatus: HttpStatusCode.NOT_FOUND_404 }
527
528 await testEndpoints({ ...options, jobToken })
529 await fetchVideoInputFiles({ ...options, jobToken, videoUUID })
530 await fetchStudioFiles({ ...options, jobToken: studioAcceptedJob.jobToken, videoUUID: videoStudioUUID, studioFile })
531 }
532
533 {
534 const options = { runnerToken, jobToken: jobToken2, expectedStatus: HttpStatusCode.NOT_FOUND_404 }
535
536 await testEndpoints({ ...options, jobUUID })
537 await fetchVideoInputFiles({ ...options, jobUUID, videoUUID })
538 await fetchStudioFiles({ ...options, jobUUID: studioAcceptedJob.uuid, videoUUID: videoStudioUUID, studioFile })
539 }
d102de1b
C
540 })
541 })
542
543 describe('Unregister', function () {
544
545 it('Should fail without a runner token', async function () {
546 await server.runners.unregister({ runnerToken: null, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
547 })
548
549 it('Should fail with a bad a runner token', async function () {
550 await server.runners.unregister({ runnerToken: '', expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
551 })
552
553 it('Should fail with an unknown runner token', async function () {
554 await server.runners.unregister({ runnerToken: badUUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
555 })
556 })
557
558 describe('Request', function () {
559
560 it('Should fail without a runner token', async function () {
561 await server.runnerJobs.request({ runnerToken: null, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
562 })
563
564 it('Should fail with a bad a runner token', async function () {
565 await server.runnerJobs.request({ runnerToken: '', expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
566 })
567
568 it('Should fail with an unknown runner token', async function () {
569 await server.runnerJobs.request({ runnerToken: badUUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
570 })
571 })
572
573 describe('Accept', function () {
574
575 it('Should fail with a bad a job uuid', async function () {
576 await server.runnerJobs.accept({ jobUUID: '', runnerToken, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
577 })
578
579 it('Should fail with an unknown job uuid', async function () {
580 await server.runnerJobs.accept({ jobUUID: badUUID, runnerToken, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
581 })
582
583 it('Should fail with a job not in pending state', async function () {
584 await server.runnerJobs.accept({ jobUUID: completedJobUUID, runnerToken, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
585 await server.runnerJobs.accept({ jobUUID: cancelledJobUUID, runnerToken, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
586 })
587
588 it('Should fail without a runner token', async function () {
589 await server.runnerJobs.accept({ jobUUID: pendingUUID, runnerToken: null, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
590 })
591
592 it('Should fail with a bad a runner token', async function () {
593 await server.runnerJobs.accept({ jobUUID: pendingUUID, runnerToken: '', expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
594 })
595
596 it('Should fail with an unknown runner token', async function () {
597 await server.runnerJobs.accept({ jobUUID: pendingUUID, runnerToken: badUUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
598 })
599 })
600
601 describe('Abort', function () {
602
603 it('Should fail without a reason', async function () {
604 await server.runnerJobs.abort({ jobUUID, jobToken, runnerToken, reason: null, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
605 })
606
607 it('Should fail with a bad reason', async function () {
608 const reason = 'reason'.repeat(5000)
609 await server.runnerJobs.abort({ jobUUID, jobToken, runnerToken, reason, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
610 })
611
612 it('Should fail with a job not in processing state', async function () {
613 await server.runnerJobs.abort({
614 jobUUID: completedJobUUID,
615 jobToken: completedJobToken,
616 runnerToken,
617 reason: 'reason',
618 expectedStatus: HttpStatusCode.BAD_REQUEST_400
619 })
620 })
621 })
622
623 describe('Update', function () {
624
625 describe('Common', function () {
626
627 it('Should fail with an invalid progress', async function () {
628 await server.runnerJobs.update({ jobUUID, jobToken, runnerToken, progress: 101, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
629 })
630
631 it('Should fail with a job not in processing state', async function () {
632 await server.runnerJobs.update({
633 jobUUID: completedJobUUID,
634 jobToken: completedJobToken,
635 runnerToken,
636 expectedStatus: HttpStatusCode.BAD_REQUEST_400
637 })
638 })
639 })
640
641 describe('Live RTMP to HLS', function () {
642 const base: RunnerJobUpdatePayload = {
643 masterPlaylistFile: 'live/master.m3u8',
644 resolutionPlaylistFilename: '0.m3u8',
645 resolutionPlaylistFile: 'live/1.m3u8',
646 type: 'add-chunk',
647 videoChunkFile: 'live/1-000069.ts',
648 videoChunkFilename: '1-000068.ts'
649 }
650
651 function testUpdate (payload: RunnerJobUpdatePayload) {
652 return server.runnerJobs.update({
653 jobUUID: liveAcceptedJob.uuid,
654 jobToken: liveAcceptedJob.jobToken,
655 payload,
656 runnerToken,
657 expectedStatus: HttpStatusCode.BAD_REQUEST_400
658 })
659 }
660
661 it('Should fail with an invalid resolutionPlaylistFilename', async function () {
662 await testUpdate({ ...base, resolutionPlaylistFilename: undefined })
663 await testUpdate({ ...base, resolutionPlaylistFilename: 'coucou/hello' })
664 await testUpdate({ ...base, resolutionPlaylistFilename: 'hello' })
665 })
666
667 it('Should fail with an invalid videoChunkFilename', async function () {
668 await testUpdate({ ...base, resolutionPlaylistFilename: undefined })
669 await testUpdate({ ...base, resolutionPlaylistFilename: 'coucou/hello' })
670 await testUpdate({ ...base, resolutionPlaylistFilename: 'hello' })
671 })
672
673 it('Should fail with an invalid type', async function () {
674 await testUpdate({ ...base, type: undefined })
675 await testUpdate({ ...base, type: 'toto' as any })
676 })
677
678 it('Should succeed with the correct params', async function () {
679 await server.runnerJobs.update({
680 jobUUID: liveAcceptedJob.uuid,
681 jobToken: liveAcceptedJob.jobToken,
682 payload: base,
683 runnerToken
684 })
685
686 await server.runnerJobs.update({
687 jobUUID: liveAcceptedJob.uuid,
688 jobToken: liveAcceptedJob.jobToken,
689 payload: { ...base, masterPlaylistFile: undefined },
690 runnerToken
691 })
692 })
693 })
694 })
695
696 describe('Error', function () {
697
698 it('Should fail with a missing error message', async function () {
699 await server.runnerJobs.error({ jobUUID, jobToken, runnerToken, message: null, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
700 })
701
702 it('Should fail with an invalid error messgae', async function () {
703 const message = 'a'.repeat(6000)
704 await server.runnerJobs.error({ jobUUID, jobToken, runnerToken, message, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
705 })
706
707 it('Should fail with a job not in processing state', async function () {
708 await server.runnerJobs.error({
709 jobUUID: completedJobUUID,
710 jobToken: completedJobToken,
711 message: 'my message',
712 runnerToken,
713 expectedStatus: HttpStatusCode.BAD_REQUEST_400
714 })
715 })
716 })
717
718 describe('Success', function () {
719 let vodJobUUID: string
720 let vodJobToken: string
721
722 describe('Common', function () {
723
724 it('Should fail with a job not in processing state', async function () {
725 await server.runnerJobs.success({
726 jobUUID: completedJobUUID,
727 jobToken: completedJobToken,
728 payload: { videoFile: 'video_short.mp4' },
729 runnerToken,
730 expectedStatus: HttpStatusCode.BAD_REQUEST_400
731 })
732 })
733 })
734
735 describe('VOD', function () {
736
737 it('Should fail with an invalid vod web video payload', async function () {
738 const { job } = await server.runnerJobs.autoAccept({ runnerToken, type: 'vod-web-video-transcoding' })
739
740 await server.runnerJobs.success({
741 jobUUID: job.uuid,
742 jobToken: job.jobToken,
743 payload: { hello: 'video_short.mp4' } as any,
744 runnerToken,
745 expectedStatus: HttpStatusCode.BAD_REQUEST_400
746 })
747
748 vodJobUUID = job.uuid
749 vodJobToken = job.jobToken
750 })
751
752 it('Should fail with an invalid vod hls payload', async function () {
753 // To create HLS jobs
754 const payload: RunnerJobSuccessPayload = { videoFile: 'video_short.mp4' }
755 await server.runnerJobs.success({ runnerToken, jobUUID: vodJobUUID, jobToken: vodJobToken, payload })
756
757 await waitJobs([ server ])
758
759 const { job } = await server.runnerJobs.autoAccept({ runnerToken, type: 'vod-hls-transcoding' })
760
761 await server.runnerJobs.success({
762 jobUUID: job.uuid,
763 jobToken: job.jobToken,
764 payload: { videoFile: 'video_short.mp4' } as any,
765 runnerToken,
766 expectedStatus: HttpStatusCode.BAD_REQUEST_400
767 })
768 })
769
770 it('Should fail with an invalid vod audio merge payload', async function () {
771 const attributes = { name: 'audio_with_preview', previewfile: 'preview.jpg', fixture: 'sample.ogg' }
772 await server.videos.upload({ attributes, mode: 'legacy' })
773
774 await waitJobs([ server ])
775
776 const { job } = await server.runnerJobs.autoAccept({ runnerToken, type: 'vod-audio-merge-transcoding' })
777
778 await server.runnerJobs.success({
779 jobUUID: job.uuid,
780 jobToken: job.jobToken,
781 payload: { hello: 'video_short.mp4' } as any,
782 runnerToken,
783 expectedStatus: HttpStatusCode.BAD_REQUEST_400
784 })
785 })
786 })
5e47f6ab
C
787
788 describe('Video studio', function () {
789
ab14f0e0 790 it('Should fail with an invalid video studio transcoding payload', async function () {
5e47f6ab
C
791 await server.runnerJobs.success({
792 jobUUID: studioAcceptedJob.uuid,
793 jobToken: studioAcceptedJob.jobToken,
794 payload: { hello: 'video_short.mp4' } as any,
795 runnerToken,
796 expectedStatus: HttpStatusCode.BAD_REQUEST_400
797 })
798 })
799 })
d102de1b
C
800 })
801
802 describe('Job files', function () {
803
5e47f6ab
C
804 describe('Check video param for common job file routes', function () {
805
806 async function fetchFiles (options: {
807 videoUUID?: string
808 expectedStatus: HttpStatusCode
809 }) {
810 await fetchVideoInputFiles({ videoUUID, ...options, jobToken, jobUUID, runnerToken })
811
812 await fetchStudioFiles({
813 videoUUID: videoStudioUUID,
814
815 ...options,
816
817 jobToken: studioAcceptedJob.jobToken,
818 jobUUID: studioAcceptedJob.uuid,
819 runnerToken,
820 studioFile
821 })
822 }
d102de1b
C
823
824 it('Should fail with an invalid video id', async function () {
5e47f6ab
C
825 await fetchFiles({
826 videoUUID: 'a',
827 expectedStatus: HttpStatusCode.BAD_REQUEST_400
828 })
d102de1b
C
829 })
830
831 it('Should fail with an unknown video id', async function () {
832 const videoUUID = '910ec12a-d9e6-458b-a274-0abb655f9464'
5e47f6ab
C
833
834 await fetchFiles({
835 videoUUID,
836 expectedStatus: HttpStatusCode.NOT_FOUND_404
837 })
d102de1b
C
838 })
839
840 it('Should fail with a video id not associated to this job', async function () {
5e47f6ab
C
841 await fetchFiles({
842 videoUUID: videoUUID2,
843 expectedStatus: HttpStatusCode.FORBIDDEN_403
844 })
d102de1b
C
845 })
846
847 it('Should succeed with the correct params', async function () {
5e47f6ab
C
848 await fetchFiles({ expectedStatus: HttpStatusCode.OK_200 })
849 })
850 })
851
ab14f0e0 852 describe('Video studio tasks file routes', function () {
5e47f6ab
C
853
854 it('Should fail with an invalid studio filename', async function () {
855 await fetchStudioFiles({
856 videoUUID: videoStudioUUID,
857 jobUUID: studioAcceptedJob.uuid,
858 runnerToken,
859 jobToken: studioAcceptedJob.jobToken,
860 studioFile: 'toto',
861 expectedStatus: HttpStatusCode.BAD_REQUEST_400
862 })
d102de1b
C
863 })
864 })
865 })
866 })
867
868 after(async function () {
869 await cleanupTests([ server ])
870 })
871})