]>
Commit | Line | Data |
---|---|---|
5e47f6ab | 1 | import { basename } from 'path' |
d102de1b C |
2 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ |
3 | import { checkBadCountPagination, checkBadSortPagination, checkBadStartPagination } from '@server/tests/shared' | |
5e47f6ab C |
4 | import { |
5 | HttpStatusCode, | |
6 | isVideoStudioTaskIntro, | |
7 | RunnerJob, | |
8 | RunnerJobState, | |
9 | RunnerJobSuccessPayload, | |
10 | RunnerJobUpdatePayload, | |
11 | RunnerJobVideoEditionTranscodingPayload, | |
12 | VideoPrivacy, | |
13 | VideoStudioTaskIntro | |
14 | } from '@shared/models' | |
d102de1b C |
15 | import { |
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 | ||
28 | const badUUID = '910ec12a-d9e6-458b-a274-0abb655f9464' | |
29 | ||
30 | describe('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 | ||
407 | const { job } = await server.runnerJobs.autoAccept({ runnerToken, type: 'video-edition-transcoding' }) | |
408 | studioAcceptedJob = job | |
409 | ||
410 | const tasks = (job.payload as RunnerJobVideoEditionTranscodingPayload).tasks | |
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 | ||
790 | it('Should fail with an invalid video edition transcoding payload', async function () { | |
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 | ||
852 | describe('Video edition tasks file routes', function () { | |
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 | }) |