diff options
Diffstat (limited to 'server/tests')
-rw-r--r-- | server/tests/api/check-params/config.ts | 5 | ||||
-rw-r--r-- | server/tests/api/check-params/runners.ts | 211 | ||||
-rw-r--r-- | server/tests/api/runners/index.ts | 1 | ||||
-rw-r--r-- | server/tests/api/runners/runner-common.ts | 12 | ||||
-rw-r--r-- | server/tests/api/runners/runner-studio-transcoding.ts | 168 | ||||
-rw-r--r-- | server/tests/api/runners/runner-vod-transcoding.ts | 16 | ||||
-rw-r--r-- | server/tests/api/server/config.ts | 7 | ||||
-rw-r--r-- | server/tests/api/transcoding/video-studio.ts | 42 | ||||
-rw-r--r-- | server/tests/peertube-runner/index.ts | 1 | ||||
-rw-r--r-- | server/tests/peertube-runner/live-transcoding.ts | 15 | ||||
-rw-r--r-- | server/tests/peertube-runner/studio-transcoding.ts | 116 | ||||
-rw-r--r-- | server/tests/peertube-runner/vod-transcoding.ts | 14 | ||||
-rw-r--r-- | server/tests/shared/checks.ts | 17 | ||||
-rw-r--r-- | server/tests/shared/directories.ts | 21 |
14 files changed, 576 insertions, 70 deletions
diff --git a/server/tests/api/check-params/config.ts b/server/tests/api/check-params/config.ts index c5cda203e..472cad182 100644 --- a/server/tests/api/check-params/config.ts +++ b/server/tests/api/check-params/config.ts | |||
@@ -162,7 +162,10 @@ describe('Test config API validators', function () { | |||
162 | } | 162 | } |
163 | }, | 163 | }, |
164 | videoStudio: { | 164 | videoStudio: { |
165 | enabled: true | 165 | enabled: true, |
166 | remoteRunners: { | ||
167 | enabled: true | ||
168 | } | ||
166 | }, | 169 | }, |
167 | import: { | 170 | import: { |
168 | videos: { | 171 | videos: { |
diff --git a/server/tests/api/check-params/runners.ts b/server/tests/api/check-params/runners.ts index 4da6fd91d..90a301392 100644 --- a/server/tests/api/check-params/runners.ts +++ b/server/tests/api/check-params/runners.ts | |||
@@ -1,6 +1,17 @@ | |||
1 | import { basename } from 'path' | ||
1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ | 2 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ |
2 | import { checkBadCountPagination, checkBadSortPagination, checkBadStartPagination } from '@server/tests/shared' | 3 | import { checkBadCountPagination, checkBadSortPagination, checkBadStartPagination } from '@server/tests/shared' |
3 | import { HttpStatusCode, RunnerJob, RunnerJobState, RunnerJobSuccessPayload, RunnerJobUpdatePayload, VideoPrivacy } from '@shared/models' | 4 | import { |
5 | HttpStatusCode, | ||
6 | isVideoStudioTaskIntro, | ||
7 | RunnerJob, | ||
8 | RunnerJobState, | ||
9 | RunnerJobSuccessPayload, | ||
10 | RunnerJobUpdatePayload, | ||
11 | RunnerJobVideoEditionTranscodingPayload, | ||
12 | VideoPrivacy, | ||
13 | VideoStudioTaskIntro | ||
14 | } from '@shared/models' | ||
4 | import { | 15 | import { |
5 | cleanupTests, | 16 | cleanupTests, |
6 | createSingleServer, | 17 | createSingleServer, |
@@ -10,6 +21,7 @@ import { | |||
10 | setAccessTokensToServers, | 21 | setAccessTokensToServers, |
11 | setDefaultVideoChannel, | 22 | setDefaultVideoChannel, |
12 | stopFfmpeg, | 23 | stopFfmpeg, |
24 | VideoStudioCommand, | ||
13 | waitJobs | 25 | waitJobs |
14 | } from '@shared/server-commands' | 26 | } from '@shared/server-commands' |
15 | 27 | ||
@@ -53,7 +65,10 @@ describe('Test managing runners', function () { | |||
53 | registrationTokenId = data[0].id | 65 | registrationTokenId = data[0].id |
54 | 66 | ||
55 | await server.config.enableTranscoding(true, true) | 67 | await server.config.enableTranscoding(true, true) |
68 | await server.config.enableStudio() | ||
56 | await server.config.enableRemoteTranscoding() | 69 | await server.config.enableRemoteTranscoding() |
70 | await server.config.enableRemoteStudio() | ||
71 | |||
57 | runnerToken = await server.runners.autoRegisterRunner() | 72 | runnerToken = await server.runners.autoRegisterRunner() |
58 | runnerToken2 = await server.runners.autoRegisterRunner() | 73 | runnerToken2 = await server.runners.autoRegisterRunner() |
59 | 74 | ||
@@ -249,6 +264,10 @@ describe('Test managing runners', function () { | |||
249 | await server.runnerJobs.cancelByAdmin({ jobUUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 }) | 264 | await server.runnerJobs.cancelByAdmin({ jobUUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 }) |
250 | }) | 265 | }) |
251 | 266 | ||
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 | |||
252 | it('Should succeed with the correct params', async function () { | 271 | it('Should succeed with the correct params', async function () { |
253 | await server.runnerJobs.cancelByAdmin({ jobUUID }) | 272 | await server.runnerJobs.cancelByAdmin({ jobUUID }) |
254 | }) | 273 | }) |
@@ -296,9 +315,13 @@ describe('Test managing runners', function () { | |||
296 | 315 | ||
297 | let pendingUUID: string | 316 | let pendingUUID: string |
298 | 317 | ||
318 | let videoStudioUUID: string | ||
319 | let studioFile: string | ||
320 | |||
299 | let liveAcceptedJob: RunnerJob & { jobToken: string } | 321 | let liveAcceptedJob: RunnerJob & { jobToken: string } |
322 | let studioAcceptedJob: RunnerJob & { jobToken: string } | ||
300 | 323 | ||
301 | async function fetchFiles (options: { | 324 | async function fetchVideoInputFiles (options: { |
302 | jobUUID: string | 325 | jobUUID: string |
303 | videoUUID: string | 326 | videoUUID: string |
304 | runnerToken: string | 327 | runnerToken: string |
@@ -315,6 +338,21 @@ describe('Test managing runners', function () { | |||
315 | } | 338 | } |
316 | } | 339 | } |
317 | 340 | ||
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 | |||
318 | before(async function () { | 356 | before(async function () { |
319 | this.timeout(120000) | 357 | this.timeout(120000) |
320 | 358 | ||
@@ -353,6 +391,28 @@ describe('Test managing runners', function () { | |||
353 | } | 391 | } |
354 | 392 | ||
355 | { | 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 | |||
415 | { | ||
356 | await server.config.enableLive({ | 416 | await server.config.enableLive({ |
357 | allowReplay: false, | 417 | allowReplay: false, |
358 | resolutions: 'max', | 418 | resolutions: 'max', |
@@ -381,8 +441,6 @@ describe('Test managing runners', function () { | |||
381 | jobToken: string | 441 | jobToken: string |
382 | expectedStatus: HttpStatusCode | 442 | expectedStatus: HttpStatusCode |
383 | }) { | 443 | }) { |
384 | await fetchFiles({ ...options, videoUUID }) | ||
385 | |||
386 | await server.runnerJobs.abort({ ...options, reason: 'reason' }) | 444 | await server.runnerJobs.abort({ ...options, reason: 'reason' }) |
387 | await server.runnerJobs.update({ ...options }) | 445 | await server.runnerJobs.update({ ...options }) |
388 | await server.runnerJobs.error({ ...options, message: 'message' }) | 446 | await server.runnerJobs.error({ ...options, message: 'message' }) |
@@ -390,39 +448,95 @@ describe('Test managing runners', function () { | |||
390 | } | 448 | } |
391 | 449 | ||
392 | it('Should fail with an invalid job uuid', async function () { | 450 | it('Should fail with an invalid job uuid', async function () { |
393 | await testEndpoints({ jobUUID: 'a', runnerToken, jobToken, expectedStatus: HttpStatusCode.BAD_REQUEST_400 }) | 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 }) | ||
394 | }) | 456 | }) |
395 | 457 | ||
396 | it('Should fail with an unknown job uuid', async function () { | 458 | it('Should fail with an unknown job uuid', async function () { |
397 | const jobUUID = badUUID | 459 | const options = { jobUUID: badUUID, runnerToken, expectedStatus: HttpStatusCode.NOT_FOUND_404 } |
398 | await testEndpoints({ jobUUID, runnerToken, jobToken, 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 }) | ||
399 | }) | 464 | }) |
400 | 465 | ||
401 | it('Should fail with an invalid runner token', async function () { | 466 | it('Should fail with an invalid runner token', async function () { |
402 | await testEndpoints({ jobUUID, runnerToken: '', jobToken, expectedStatus: HttpStatusCode.BAD_REQUEST_400 }) | 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 | }) | ||
403 | }) | 478 | }) |
404 | 479 | ||
405 | it('Should fail with an unknown runner token', async function () { | 480 | it('Should fail with an unknown runner token', async function () { |
406 | const runnerToken = badUUID | 481 | const options = { runnerToken: badUUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 } |
407 | await testEndpoints({ jobUUID, runnerToken, jobToken, 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 | }) | ||
408 | }) | 492 | }) |
409 | 493 | ||
410 | it('Should fail with an invalid job token job uuid', async function () { | 494 | it('Should fail with an invalid job token job uuid', async function () { |
411 | await testEndpoints({ jobUUID, runnerToken, jobToken: '', expectedStatus: HttpStatusCode.BAD_REQUEST_400 }) | 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 }) | ||
412 | }) | 500 | }) |
413 | 501 | ||
414 | it('Should fail with an unknown job token job uuid', async function () { | 502 | it('Should fail with an unknown job token job uuid', async function () { |
415 | const jobToken = badUUID | 503 | const options = { runnerToken, jobToken: badUUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 } |
416 | await testEndpoints({ jobUUID, runnerToken, jobToken, 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 }) | ||
417 | }) | 508 | }) |
418 | 509 | ||
419 | it('Should fail with a runner token not associated to this job', async function () { | 510 | it('Should fail with a runner token not associated to this job', async function () { |
420 | await testEndpoints({ jobUUID, runnerToken: runnerToken2, jobToken, expectedStatus: HttpStatusCode.NOT_FOUND_404 }) | 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 | }) | ||
421 | }) | 522 | }) |
422 | 523 | ||
423 | it('Should fail with a job uuid not associated to the job token', async function () { | 524 | it('Should fail with a job uuid not associated to the job token', async function () { |
424 | await testEndpoints({ jobUUID: jobUUID2, runnerToken, jobToken, expectedStatus: HttpStatusCode.NOT_FOUND_404 }) | 525 | { |
425 | await testEndpoints({ jobUUID, runnerToken, jobToken: jobToken2, expectedStatus: HttpStatusCode.NOT_FOUND_404 }) | 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 | } | ||
426 | }) | 540 | }) |
427 | }) | 541 | }) |
428 | 542 | ||
@@ -670,27 +784,82 @@ describe('Test managing runners', function () { | |||
670 | }) | 784 | }) |
671 | }) | 785 | }) |
672 | }) | 786 | }) |
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 | }) | ||
673 | }) | 800 | }) |
674 | 801 | ||
675 | describe('Job files', function () { | 802 | describe('Job files', function () { |
676 | 803 | ||
677 | describe('Video files', function () { | 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 | } | ||
678 | 823 | ||
679 | it('Should fail with an invalid video id', async function () { | 824 | it('Should fail with an invalid video id', async function () { |
680 | await fetchFiles({ videoUUID: 'a', jobUUID, runnerToken, jobToken, expectedStatus: HttpStatusCode.BAD_REQUEST_400 }) | 825 | await fetchFiles({ |
826 | videoUUID: 'a', | ||
827 | expectedStatus: HttpStatusCode.BAD_REQUEST_400 | ||
828 | }) | ||
681 | }) | 829 | }) |
682 | 830 | ||
683 | it('Should fail with an unknown video id', async function () { | 831 | it('Should fail with an unknown video id', async function () { |
684 | const videoUUID = '910ec12a-d9e6-458b-a274-0abb655f9464' | 832 | const videoUUID = '910ec12a-d9e6-458b-a274-0abb655f9464' |
685 | await fetchFiles({ videoUUID, jobUUID, runnerToken, jobToken, expectedStatus: HttpStatusCode.NOT_FOUND_404 }) | 833 | |
834 | await fetchFiles({ | ||
835 | videoUUID, | ||
836 | expectedStatus: HttpStatusCode.NOT_FOUND_404 | ||
837 | }) | ||
686 | }) | 838 | }) |
687 | 839 | ||
688 | it('Should fail with a video id not associated to this job', async function () { | 840 | it('Should fail with a video id not associated to this job', async function () { |
689 | await fetchFiles({ videoUUID: videoUUID2, jobUUID, runnerToken, jobToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) | 841 | await fetchFiles({ |
842 | videoUUID: videoUUID2, | ||
843 | expectedStatus: HttpStatusCode.FORBIDDEN_403 | ||
844 | }) | ||
690 | }) | 845 | }) |
691 | 846 | ||
692 | it('Should succeed with the correct params', async function () { | 847 | it('Should succeed with the correct params', async function () { |
693 | await fetchFiles({ videoUUID, jobUUID, runnerToken, jobToken, expectedStatus: HttpStatusCode.OK_200 }) | 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 | }) | ||
694 | }) | 863 | }) |
695 | }) | 864 | }) |
696 | }) | 865 | }) |
diff --git a/server/tests/api/runners/index.ts b/server/tests/api/runners/index.ts index 7f33ec8dd..642a3a96d 100644 --- a/server/tests/api/runners/index.ts +++ b/server/tests/api/runners/index.ts | |||
@@ -1,4 +1,5 @@ | |||
1 | export * from './runner-common' | 1 | export * from './runner-common' |
2 | export * from './runner-live-transcoding' | 2 | export * from './runner-live-transcoding' |
3 | export * from './runner-socket' | 3 | export * from './runner-socket' |
4 | export * from './runner-studio-transcoding' | ||
4 | export * from './runner-vod-transcoding' | 5 | export * from './runner-vod-transcoding' |
diff --git a/server/tests/api/runners/runner-common.ts b/server/tests/api/runners/runner-common.ts index a2204753b..554024190 100644 --- a/server/tests/api/runners/runner-common.ts +++ b/server/tests/api/runners/runner-common.ts | |||
@@ -2,7 +2,15 @@ | |||
2 | 2 | ||
3 | import { expect } from 'chai' | 3 | import { expect } from 'chai' |
4 | import { wait } from '@shared/core-utils' | 4 | import { wait } from '@shared/core-utils' |
5 | import { HttpStatusCode, Runner, RunnerJob, RunnerJobAdmin, RunnerJobState, RunnerRegistrationToken } from '@shared/models' | 5 | import { |
6 | HttpStatusCode, | ||
7 | Runner, | ||
8 | RunnerJob, | ||
9 | RunnerJobAdmin, | ||
10 | RunnerJobState, | ||
11 | RunnerJobVODWebVideoTranscodingPayload, | ||
12 | RunnerRegistrationToken | ||
13 | } from '@shared/models' | ||
6 | import { | 14 | import { |
7 | cleanupTests, | 15 | cleanupTests, |
8 | createSingleServer, | 16 | createSingleServer, |
@@ -349,7 +357,7 @@ describe('Test runner common actions', function () { | |||
349 | for (const job of availableJobs) { | 357 | for (const job of availableJobs) { |
350 | expect(job.uuid).to.exist | 358 | expect(job.uuid).to.exist |
351 | expect(job.payload.input).to.exist | 359 | expect(job.payload.input).to.exist |
352 | expect(job.payload.output).to.exist | 360 | expect((job.payload as RunnerJobVODWebVideoTranscodingPayload).output).to.exist |
353 | 361 | ||
354 | expect((job as RunnerJobAdmin).privatePayload).to.not.exist | 362 | expect((job as RunnerJobAdmin).privatePayload).to.not.exist |
355 | } | 363 | } |
diff --git a/server/tests/api/runners/runner-studio-transcoding.ts b/server/tests/api/runners/runner-studio-transcoding.ts new file mode 100644 index 000000000..9ae629be6 --- /dev/null +++ b/server/tests/api/runners/runner-studio-transcoding.ts | |||
@@ -0,0 +1,168 @@ | |||
1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ | ||
2 | |||
3 | import { expect } from 'chai' | ||
4 | import { readFile } from 'fs-extra' | ||
5 | import { checkPersistentTmpIsEmpty, checkVideoDuration } from '@server/tests/shared' | ||
6 | import { buildAbsoluteFixturePath } from '@shared/core-utils' | ||
7 | import { | ||
8 | RunnerJobVideoEditionTranscodingPayload, | ||
9 | VideoEditionTranscodingSuccess, | ||
10 | VideoState, | ||
11 | VideoStudioTask, | ||
12 | VideoStudioTaskIntro | ||
13 | } from '@shared/models' | ||
14 | import { | ||
15 | cleanupTests, | ||
16 | createMultipleServers, | ||
17 | doubleFollow, | ||
18 | PeerTubeServer, | ||
19 | setAccessTokensToServers, | ||
20 | setDefaultVideoChannel, | ||
21 | VideoStudioCommand, | ||
22 | waitJobs | ||
23 | } from '@shared/server-commands' | ||
24 | |||
25 | describe('Test runner video studio transcoding', function () { | ||
26 | let servers: PeerTubeServer[] = [] | ||
27 | let runnerToken: string | ||
28 | let videoUUID: string | ||
29 | let jobUUID: string | ||
30 | |||
31 | async function renewStudio (tasks: VideoStudioTask[] = VideoStudioCommand.getComplexTask()) { | ||
32 | const { uuid } = await servers[0].videos.quickUpload({ name: 'video' }) | ||
33 | videoUUID = uuid | ||
34 | |||
35 | await waitJobs(servers) | ||
36 | |||
37 | await servers[0].videoStudio.createEditionTasks({ videoId: uuid, tasks }) | ||
38 | await waitJobs(servers) | ||
39 | |||
40 | const { availableJobs } = await servers[0].runnerJobs.request({ runnerToken }) | ||
41 | expect(availableJobs).to.have.lengthOf(1) | ||
42 | |||
43 | jobUUID = availableJobs[0].uuid | ||
44 | } | ||
45 | |||
46 | before(async function () { | ||
47 | this.timeout(120_000) | ||
48 | |||
49 | servers = await createMultipleServers(2) | ||
50 | |||
51 | await setAccessTokensToServers(servers) | ||
52 | await setDefaultVideoChannel(servers) | ||
53 | |||
54 | await doubleFollow(servers[0], servers[1]) | ||
55 | |||
56 | await servers[0].config.enableTranscoding(true, true) | ||
57 | await servers[0].config.enableStudio() | ||
58 | await servers[0].config.enableRemoteStudio() | ||
59 | |||
60 | runnerToken = await servers[0].runners.autoRegisterRunner() | ||
61 | }) | ||
62 | |||
63 | it('Should error a studio transcoding job', async function () { | ||
64 | this.timeout(60000) | ||
65 | |||
66 | await renewStudio() | ||
67 | |||
68 | for (let i = 0; i < 5; i++) { | ||
69 | const { job } = await servers[0].runnerJobs.accept({ runnerToken, jobUUID }) | ||
70 | const jobToken = job.jobToken | ||
71 | |||
72 | await servers[0].runnerJobs.error({ runnerToken, jobUUID, jobToken, message: 'Error' }) | ||
73 | } | ||
74 | |||
75 | const video = await servers[0].videos.get({ id: videoUUID }) | ||
76 | expect(video.state.id).to.equal(VideoState.PUBLISHED) | ||
77 | |||
78 | await checkPersistentTmpIsEmpty(servers[0]) | ||
79 | }) | ||
80 | |||
81 | it('Should cancel a transcoding job', async function () { | ||
82 | this.timeout(60000) | ||
83 | |||
84 | await renewStudio() | ||
85 | |||
86 | await servers[0].runnerJobs.cancelByAdmin({ jobUUID }) | ||
87 | |||
88 | const video = await servers[0].videos.get({ id: videoUUID }) | ||
89 | expect(video.state.id).to.equal(VideoState.PUBLISHED) | ||
90 | |||
91 | await checkPersistentTmpIsEmpty(servers[0]) | ||
92 | }) | ||
93 | |||
94 | it('Should execute a remote studio job', async function () { | ||
95 | this.timeout(240_000) | ||
96 | |||
97 | const tasks = [ | ||
98 | { | ||
99 | name: 'add-outro' as 'add-outro', | ||
100 | options: { | ||
101 | file: 'video_short.webm' | ||
102 | } | ||
103 | }, | ||
104 | { | ||
105 | name: 'add-watermark' as 'add-watermark', | ||
106 | options: { | ||
107 | file: 'thumbnail.png' | ||
108 | } | ||
109 | }, | ||
110 | { | ||
111 | name: 'add-intro' as 'add-intro', | ||
112 | options: { | ||
113 | file: 'video_very_short_240p.mp4' | ||
114 | } | ||
115 | } | ||
116 | ] | ||
117 | |||
118 | await renewStudio(tasks) | ||
119 | |||
120 | for (const server of servers) { | ||
121 | await checkVideoDuration(server, videoUUID, 5) | ||
122 | } | ||
123 | |||
124 | const { job } = await servers[0].runnerJobs.accept<RunnerJobVideoEditionTranscodingPayload>({ runnerToken, jobUUID }) | ||
125 | const jobToken = job.jobToken | ||
126 | |||
127 | expect(job.type === 'video-edition-transcoding') | ||
128 | expect(job.payload.input.videoFileUrl).to.exist | ||
129 | |||
130 | // Check video input file | ||
131 | { | ||
132 | await servers[0].runnerJobs.getJobFile({ url: job.payload.input.videoFileUrl, jobToken, runnerToken }) | ||
133 | } | ||
134 | |||
135 | // Check task files | ||
136 | for (let i = 0; i < tasks.length; i++) { | ||
137 | const task = tasks[i] | ||
138 | const payloadTask = job.payload.tasks[i] | ||
139 | |||
140 | expect(payloadTask.name).to.equal(task.name) | ||
141 | |||
142 | const inputFile = await readFile(buildAbsoluteFixturePath(task.options.file)) | ||
143 | |||
144 | const { body } = await servers[0].runnerJobs.getJobFile({ | ||
145 | url: (payloadTask as VideoStudioTaskIntro).options.file as string, | ||
146 | jobToken, | ||
147 | runnerToken | ||
148 | }) | ||
149 | |||
150 | expect(body).to.deep.equal(inputFile) | ||
151 | } | ||
152 | |||
153 | const payload: VideoEditionTranscodingSuccess = { videoFile: 'video_very_short_240p.mp4' } | ||
154 | await servers[0].runnerJobs.success({ runnerToken, jobUUID, jobToken, payload }) | ||
155 | |||
156 | await waitJobs(servers) | ||
157 | |||
158 | for (const server of servers) { | ||
159 | await checkVideoDuration(server, videoUUID, 2) | ||
160 | } | ||
161 | |||
162 | await checkPersistentTmpIsEmpty(servers[0]) | ||
163 | }) | ||
164 | |||
165 | after(async function () { | ||
166 | await cleanupTests(servers) | ||
167 | }) | ||
168 | }) | ||
diff --git a/server/tests/api/runners/runner-vod-transcoding.ts b/server/tests/api/runners/runner-vod-transcoding.ts index 92a47ac3b..b08ee312c 100644 --- a/server/tests/api/runners/runner-vod-transcoding.ts +++ b/server/tests/api/runners/runner-vod-transcoding.ts | |||
@@ -155,7 +155,7 @@ describe('Test runner VOD transcoding', function () { | |||
155 | expect(job.payload.output.resolution).to.equal(720) | 155 | expect(job.payload.output.resolution).to.equal(720) |
156 | expect(job.payload.output.fps).to.equal(25) | 156 | expect(job.payload.output.fps).to.equal(25) |
157 | 157 | ||
158 | const { body } = await servers[0].runnerJobs.getInputFile({ url: job.payload.input.videoFileUrl, jobToken, runnerToken }) | 158 | const { body } = await servers[0].runnerJobs.getJobFile({ url: job.payload.input.videoFileUrl, jobToken, runnerToken }) |
159 | const inputFile = await readFile(buildAbsoluteFixturePath('video_short.webm')) | 159 | const inputFile = await readFile(buildAbsoluteFixturePath('video_short.webm')) |
160 | 160 | ||
161 | expect(body).to.deep.equal(inputFile) | 161 | expect(body).to.deep.equal(inputFile) |
@@ -200,7 +200,7 @@ describe('Test runner VOD transcoding', function () { | |||
200 | const { job } = await servers[0].runnerJobs.accept<RunnerJobVODWebVideoTranscodingPayload>({ runnerToken, jobUUID }) | 200 | const { job } = await servers[0].runnerJobs.accept<RunnerJobVODWebVideoTranscodingPayload>({ runnerToken, jobUUID }) |
201 | jobToken = job.jobToken | 201 | jobToken = job.jobToken |
202 | 202 | ||
203 | const { body } = await servers[0].runnerJobs.getInputFile({ url: job.payload.input.videoFileUrl, jobToken, runnerToken }) | 203 | const { body } = await servers[0].runnerJobs.getJobFile({ url: job.payload.input.videoFileUrl, jobToken, runnerToken }) |
204 | const inputFile = await readFile(buildAbsoluteFixturePath('video_short.mp4')) | 204 | const inputFile = await readFile(buildAbsoluteFixturePath('video_short.mp4')) |
205 | 205 | ||
206 | expect(body).to.deep.equal(inputFile) | 206 | expect(body).to.deep.equal(inputFile) |
@@ -221,7 +221,7 @@ describe('Test runner VOD transcoding', function () { | |||
221 | const { job } = await servers[0].runnerJobs.accept<RunnerJobVODWebVideoTranscodingPayload>({ runnerToken, jobUUID }) | 221 | const { job } = await servers[0].runnerJobs.accept<RunnerJobVODWebVideoTranscodingPayload>({ runnerToken, jobUUID }) |
222 | jobToken = job.jobToken | 222 | jobToken = job.jobToken |
223 | 223 | ||
224 | const { body } = await servers[0].runnerJobs.getInputFile({ url: job.payload.input.videoFileUrl, jobToken, runnerToken }) | 224 | const { body } = await servers[0].runnerJobs.getJobFile({ url: job.payload.input.videoFileUrl, jobToken, runnerToken }) |
225 | const inputFile = await readFile(buildAbsoluteFixturePath('video_short.mp4')) | 225 | const inputFile = await readFile(buildAbsoluteFixturePath('video_short.mp4')) |
226 | expect(body).to.deep.equal(inputFile) | 226 | expect(body).to.deep.equal(inputFile) |
227 | 227 | ||
@@ -293,7 +293,7 @@ describe('Test runner VOD transcoding', function () { | |||
293 | const { job } = await servers[0].runnerJobs.accept<RunnerJobVODHLSTranscodingPayload>({ runnerToken, jobUUID }) | 293 | const { job } = await servers[0].runnerJobs.accept<RunnerJobVODHLSTranscodingPayload>({ runnerToken, jobUUID }) |
294 | jobToken = job.jobToken | 294 | jobToken = job.jobToken |
295 | 295 | ||
296 | const { body } = await servers[0].runnerJobs.getInputFile({ url: job.payload.input.videoFileUrl, jobToken, runnerToken }) | 296 | const { body } = await servers[0].runnerJobs.getJobFile({ url: job.payload.input.videoFileUrl, jobToken, runnerToken }) |
297 | const inputFile = await readFile(buildAbsoluteFixturePath('video_short.mp4')) | 297 | const inputFile = await readFile(buildAbsoluteFixturePath('video_short.mp4')) |
298 | 298 | ||
299 | expect(body).to.deep.equal(inputFile) | 299 | expect(body).to.deep.equal(inputFile) |
@@ -337,7 +337,7 @@ describe('Test runner VOD transcoding', function () { | |||
337 | const { job } = await servers[0].runnerJobs.accept<RunnerJobVODHLSTranscodingPayload>({ runnerToken, jobUUID }) | 337 | const { job } = await servers[0].runnerJobs.accept<RunnerJobVODHLSTranscodingPayload>({ runnerToken, jobUUID }) |
338 | jobToken = job.jobToken | 338 | jobToken = job.jobToken |
339 | 339 | ||
340 | const { body } = await servers[0].runnerJobs.getInputFile({ url: job.payload.input.videoFileUrl, jobToken, runnerToken }) | 340 | const { body } = await servers[0].runnerJobs.getJobFile({ url: job.payload.input.videoFileUrl, jobToken, runnerToken }) |
341 | const inputFile = await readFile(buildAbsoluteFixturePath(maxQualityFile)) | 341 | const inputFile = await readFile(buildAbsoluteFixturePath(maxQualityFile)) |
342 | expect(body).to.deep.equal(inputFile) | 342 | expect(body).to.deep.equal(inputFile) |
343 | 343 | ||
@@ -446,13 +446,13 @@ describe('Test runner VOD transcoding', function () { | |||
446 | expect(job.payload.output.resolution).to.equal(480) | 446 | expect(job.payload.output.resolution).to.equal(480) |
447 | 447 | ||
448 | { | 448 | { |
449 | const { body } = await servers[0].runnerJobs.getInputFile({ url: job.payload.input.audioFileUrl, jobToken, runnerToken }) | 449 | const { body } = await servers[0].runnerJobs.getJobFile({ url: job.payload.input.audioFileUrl, jobToken, runnerToken }) |
450 | const inputFile = await readFile(buildAbsoluteFixturePath('sample.ogg')) | 450 | const inputFile = await readFile(buildAbsoluteFixturePath('sample.ogg')) |
451 | expect(body).to.deep.equal(inputFile) | 451 | expect(body).to.deep.equal(inputFile) |
452 | } | 452 | } |
453 | 453 | ||
454 | { | 454 | { |
455 | const { body } = await servers[0].runnerJobs.getInputFile({ url: job.payload.input.previewFileUrl, jobToken, runnerToken }) | 455 | const { body } = await servers[0].runnerJobs.getJobFile({ url: job.payload.input.previewFileUrl, jobToken, runnerToken }) |
456 | 456 | ||
457 | const video = await servers[0].videos.get({ id: videoUUID }) | 457 | const video = await servers[0].videos.get({ id: videoUUID }) |
458 | const { body: inputFile } = await makeGetRequest({ | 458 | const { body: inputFile } = await makeGetRequest({ |
@@ -503,7 +503,7 @@ describe('Test runner VOD transcoding', function () { | |||
503 | const { job } = await servers[0].runnerJobs.accept<RunnerJobVODHLSTranscodingPayload>({ runnerToken, jobUUID }) | 503 | const { job } = await servers[0].runnerJobs.accept<RunnerJobVODHLSTranscodingPayload>({ runnerToken, jobUUID }) |
504 | jobToken = job.jobToken | 504 | jobToken = job.jobToken |
505 | 505 | ||
506 | const { body } = await servers[0].runnerJobs.getInputFile({ url: job.payload.input.videoFileUrl, jobToken, runnerToken }) | 506 | const { body } = await servers[0].runnerJobs.getJobFile({ url: job.payload.input.videoFileUrl, jobToken, runnerToken }) |
507 | const inputFile = await readFile(buildAbsoluteFixturePath('video_short_480p.mp4')) | 507 | const inputFile = await readFile(buildAbsoluteFixturePath('video_short_480p.mp4')) |
508 | expect(body).to.deep.equal(inputFile) | 508 | expect(body).to.deep.equal(inputFile) |
509 | 509 | ||
diff --git a/server/tests/api/server/config.ts b/server/tests/api/server/config.ts index 54a40b994..011ba268c 100644 --- a/server/tests/api/server/config.ts +++ b/server/tests/api/server/config.ts | |||
@@ -102,6 +102,7 @@ function checkInitialConfig (server: PeerTubeServer, data: CustomConfig) { | |||
102 | expect(data.live.transcoding.alwaysTranscodeOriginalResolution).to.be.true | 102 | expect(data.live.transcoding.alwaysTranscodeOriginalResolution).to.be.true |
103 | 103 | ||
104 | expect(data.videoStudio.enabled).to.be.false | 104 | expect(data.videoStudio.enabled).to.be.false |
105 | expect(data.videoStudio.remoteRunners.enabled).to.be.false | ||
105 | 106 | ||
106 | expect(data.import.videos.concurrency).to.equal(2) | 107 | expect(data.import.videos.concurrency).to.equal(2) |
107 | expect(data.import.videos.http.enabled).to.be.true | 108 | expect(data.import.videos.http.enabled).to.be.true |
@@ -211,6 +212,7 @@ function checkUpdatedConfig (data: CustomConfig) { | |||
211 | expect(data.live.transcoding.alwaysTranscodeOriginalResolution).to.be.false | 212 | expect(data.live.transcoding.alwaysTranscodeOriginalResolution).to.be.false |
212 | 213 | ||
213 | expect(data.videoStudio.enabled).to.be.true | 214 | expect(data.videoStudio.enabled).to.be.true |
215 | expect(data.videoStudio.remoteRunners.enabled).to.be.true | ||
214 | 216 | ||
215 | expect(data.import.videos.concurrency).to.equal(4) | 217 | expect(data.import.videos.concurrency).to.equal(4) |
216 | expect(data.import.videos.http.enabled).to.be.false | 218 | expect(data.import.videos.http.enabled).to.be.false |
@@ -374,7 +376,10 @@ const newCustomConfig: CustomConfig = { | |||
374 | } | 376 | } |
375 | }, | 377 | }, |
376 | videoStudio: { | 378 | videoStudio: { |
377 | enabled: true | 379 | enabled: true, |
380 | remoteRunners: { | ||
381 | enabled: true | ||
382 | } | ||
378 | }, | 383 | }, |
379 | import: { | 384 | import: { |
380 | videos: { | 385 | videos: { |
diff --git a/server/tests/api/transcoding/video-studio.ts b/server/tests/api/transcoding/video-studio.ts index 30f72e6e9..2f64ef6bd 100644 --- a/server/tests/api/transcoding/video-studio.ts +++ b/server/tests/api/transcoding/video-studio.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | import { expect } from 'chai' | 1 | import { expect } from 'chai' |
2 | import { checkPersistentTmpIsEmpty, expectStartWith } from '@server/tests/shared' | 2 | import { checkPersistentTmpIsEmpty, checkVideoDuration, expectStartWith } from '@server/tests/shared' |
3 | import { areMockObjectStorageTestsDisabled, getAllFiles } from '@shared/core-utils' | 3 | import { areMockObjectStorageTestsDisabled, getAllFiles } from '@shared/core-utils' |
4 | import { VideoStudioTask } from '@shared/models' | 4 | import { VideoStudioTask } from '@shared/models' |
5 | import { | 5 | import { |
@@ -18,20 +18,6 @@ describe('Test video studio', function () { | |||
18 | let servers: PeerTubeServer[] = [] | 18 | let servers: PeerTubeServer[] = [] |
19 | let videoUUID: string | 19 | let videoUUID: string |
20 | 20 | ||
21 | async function checkDuration (server: PeerTubeServer, duration: number) { | ||
22 | const video = await server.videos.get({ id: videoUUID }) | ||
23 | |||
24 | expect(video.duration).to.be.approximately(duration, 1) | ||
25 | |||
26 | for (const file of video.files) { | ||
27 | const metadata = await server.videos.getFileMetadata({ url: file.metadataUrl }) | ||
28 | |||
29 | for (const stream of metadata.streams) { | ||
30 | expect(Math.round(stream.duration)).to.be.approximately(duration, 1) | ||
31 | } | ||
32 | } | ||
33 | } | ||
34 | |||
35 | async function renewVideo (fixture = 'video_short.webm') { | 21 | async function renewVideo (fixture = 'video_short.webm') { |
36 | const video = await servers[0].videos.quickUpload({ name: 'video', fixture }) | 22 | const video = await servers[0].videos.quickUpload({ name: 'video', fixture }) |
37 | videoUUID = video.uuid | 23 | videoUUID = video.uuid |
@@ -79,7 +65,7 @@ describe('Test video studio', function () { | |||
79 | ]) | 65 | ]) |
80 | 66 | ||
81 | for (const server of servers) { | 67 | for (const server of servers) { |
82 | await checkDuration(server, 3) | 68 | await checkVideoDuration(server, videoUUID, 3) |
83 | 69 | ||
84 | const video = await server.videos.get({ id: videoUUID }) | 70 | const video = await server.videos.get({ id: videoUUID }) |
85 | expect(new Date(video.publishedAt)).to.be.below(beforeTasks) | 71 | expect(new Date(video.publishedAt)).to.be.below(beforeTasks) |
@@ -100,7 +86,7 @@ describe('Test video studio', function () { | |||
100 | ]) | 86 | ]) |
101 | 87 | ||
102 | for (const server of servers) { | 88 | for (const server of servers) { |
103 | await checkDuration(server, 2) | 89 | await checkVideoDuration(server, videoUUID, 2) |
104 | } | 90 | } |
105 | }) | 91 | }) |
106 | 92 | ||
@@ -119,7 +105,7 @@ describe('Test video studio', function () { | |||
119 | ]) | 105 | ]) |
120 | 106 | ||
121 | for (const server of servers) { | 107 | for (const server of servers) { |
122 | await checkDuration(server, 4) | 108 | await checkVideoDuration(server, videoUUID, 4) |
123 | } | 109 | } |
124 | }) | 110 | }) |
125 | }) | 111 | }) |
@@ -140,7 +126,7 @@ describe('Test video studio', function () { | |||
140 | ]) | 126 | ]) |
141 | 127 | ||
142 | for (const server of servers) { | 128 | for (const server of servers) { |
143 | await checkDuration(server, 10) | 129 | await checkVideoDuration(server, videoUUID, 10) |
144 | } | 130 | } |
145 | }) | 131 | }) |
146 | 132 | ||
@@ -158,7 +144,7 @@ describe('Test video studio', function () { | |||
158 | ]) | 144 | ]) |
159 | 145 | ||
160 | for (const server of servers) { | 146 | for (const server of servers) { |
161 | await checkDuration(server, 7) | 147 | await checkVideoDuration(server, videoUUID, 7) |
162 | } | 148 | } |
163 | }) | 149 | }) |
164 | 150 | ||
@@ -183,7 +169,7 @@ describe('Test video studio', function () { | |||
183 | ]) | 169 | ]) |
184 | 170 | ||
185 | for (const server of servers) { | 171 | for (const server of servers) { |
186 | await checkDuration(server, 12) | 172 | await checkVideoDuration(server, videoUUID, 12) |
187 | } | 173 | } |
188 | }) | 174 | }) |
189 | 175 | ||
@@ -201,7 +187,7 @@ describe('Test video studio', function () { | |||
201 | ]) | 187 | ]) |
202 | 188 | ||
203 | for (const server of servers) { | 189 | for (const server of servers) { |
204 | await checkDuration(server, 7) | 190 | await checkVideoDuration(server, videoUUID, 7) |
205 | } | 191 | } |
206 | }) | 192 | }) |
207 | 193 | ||
@@ -219,7 +205,7 @@ describe('Test video studio', function () { | |||
219 | ]) | 205 | ]) |
220 | 206 | ||
221 | for (const server of servers) { | 207 | for (const server of servers) { |
222 | await checkDuration(server, 10) | 208 | await checkVideoDuration(server, videoUUID, 10) |
223 | } | 209 | } |
224 | }) | 210 | }) |
225 | 211 | ||
@@ -237,7 +223,7 @@ describe('Test video studio', function () { | |||
237 | ]) | 223 | ]) |
238 | 224 | ||
239 | for (const server of servers) { | 225 | for (const server of servers) { |
240 | await checkDuration(server, 10) | 226 | await checkVideoDuration(server, videoUUID, 10) |
241 | } | 227 | } |
242 | }) | 228 | }) |
243 | }) | 229 | }) |
@@ -279,7 +265,7 @@ describe('Test video studio', function () { | |||
279 | await createTasks(VideoStudioCommand.getComplexTask()) | 265 | await createTasks(VideoStudioCommand.getComplexTask()) |
280 | 266 | ||
281 | for (const server of servers) { | 267 | for (const server of servers) { |
282 | await checkDuration(server, 9) | 268 | await checkVideoDuration(server, videoUUID, 9) |
283 | } | 269 | } |
284 | }) | 270 | }) |
285 | }) | 271 | }) |
@@ -309,7 +295,7 @@ describe('Test video studio', function () { | |||
309 | const video = await server.videos.get({ id: videoUUID }) | 295 | const video = await server.videos.get({ id: videoUUID }) |
310 | expect(video.files).to.have.lengthOf(0) | 296 | expect(video.files).to.have.lengthOf(0) |
311 | 297 | ||
312 | await checkDuration(server, 9) | 298 | await checkVideoDuration(server, videoUUID, 9) |
313 | } | 299 | } |
314 | }) | 300 | }) |
315 | }) | 301 | }) |
@@ -351,7 +337,7 @@ describe('Test video studio', function () { | |||
351 | expectStartWith(hlsFile.fileUrl, ObjectStorageCommand.getMockPlaylistBaseUrl()) | 337 | expectStartWith(hlsFile.fileUrl, ObjectStorageCommand.getMockPlaylistBaseUrl()) |
352 | } | 338 | } |
353 | 339 | ||
354 | await checkDuration(server, 9) | 340 | await checkVideoDuration(server, videoUUID, 9) |
355 | } | 341 | } |
356 | }) | 342 | }) |
357 | }) | 343 | }) |
@@ -370,7 +356,7 @@ describe('Test video studio', function () { | |||
370 | await waitJobs(servers) | 356 | await waitJobs(servers) |
371 | 357 | ||
372 | for (const server of servers) { | 358 | for (const server of servers) { |
373 | await checkDuration(server, 9) | 359 | await checkVideoDuration(server, videoUUID, 9) |
374 | } | 360 | } |
375 | }) | 361 | }) |
376 | 362 | ||
diff --git a/server/tests/peertube-runner/index.ts b/server/tests/peertube-runner/index.ts index 6258d6eb2..470316417 100644 --- a/server/tests/peertube-runner/index.ts +++ b/server/tests/peertube-runner/index.ts | |||
@@ -1,3 +1,4 @@ | |||
1 | export * from './client-cli' | 1 | export * from './client-cli' |
2 | export * from './live-transcoding' | 2 | export * from './live-transcoding' |
3 | export * from './studio-transcoding' | ||
3 | export * from './vod-transcoding' | 4 | export * from './vod-transcoding' |
diff --git a/server/tests/peertube-runner/live-transcoding.ts b/server/tests/peertube-runner/live-transcoding.ts index f58e920ba..1e94eabcd 100644 --- a/server/tests/peertube-runner/live-transcoding.ts +++ b/server/tests/peertube-runner/live-transcoding.ts | |||
@@ -1,6 +1,12 @@ | |||
1 | import { expect } from 'chai' | 1 | import { expect } from 'chai' |
2 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ | 2 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ |
3 | import { expectStartWith, PeerTubeRunnerProcess, SQLCommand, testLiveVideoResolutions } from '@server/tests/shared' | 3 | import { |
4 | checkPeerTubeRunnerCacheIsEmpty, | ||
5 | expectStartWith, | ||
6 | PeerTubeRunnerProcess, | ||
7 | SQLCommand, | ||
8 | testLiveVideoResolutions | ||
9 | } from '@server/tests/shared' | ||
4 | import { areMockObjectStorageTestsDisabled, wait } from '@shared/core-utils' | 10 | import { areMockObjectStorageTestsDisabled, wait } from '@shared/core-utils' |
5 | import { HttpStatusCode, VideoPrivacy } from '@shared/models' | 11 | import { HttpStatusCode, VideoPrivacy } from '@shared/models' |
6 | import { | 12 | import { |
@@ -169,6 +175,13 @@ describe('Test Live transcoding in peertube-runner program', function () { | |||
169 | runSuite({ objectStorage: true }) | 175 | runSuite({ objectStorage: true }) |
170 | }) | 176 | }) |
171 | 177 | ||
178 | describe('Check cleanup', function () { | ||
179 | |||
180 | it('Should have an empty cache directory', async function () { | ||
181 | await checkPeerTubeRunnerCacheIsEmpty() | ||
182 | }) | ||
183 | }) | ||
184 | |||
172 | after(async function () { | 185 | after(async function () { |
173 | await peertubeRunner.unregisterPeerTubeInstance({ server: servers[0] }) | 186 | await peertubeRunner.unregisterPeerTubeInstance({ server: servers[0] }) |
174 | peertubeRunner.kill() | 187 | peertubeRunner.kill() |
diff --git a/server/tests/peertube-runner/studio-transcoding.ts b/server/tests/peertube-runner/studio-transcoding.ts new file mode 100644 index 000000000..cca905e2f --- /dev/null +++ b/server/tests/peertube-runner/studio-transcoding.ts | |||
@@ -0,0 +1,116 @@ | |||
1 | |||
2 | import { expect } from 'chai' | ||
3 | import { checkPeerTubeRunnerCacheIsEmpty, checkVideoDuration, expectStartWith, PeerTubeRunnerProcess } from '@server/tests/shared' | ||
4 | import { areMockObjectStorageTestsDisabled, getAllFiles, wait } from '@shared/core-utils' | ||
5 | import { | ||
6 | cleanupTests, | ||
7 | createMultipleServers, | ||
8 | doubleFollow, | ||
9 | ObjectStorageCommand, | ||
10 | PeerTubeServer, | ||
11 | setAccessTokensToServers, | ||
12 | setDefaultVideoChannel, | ||
13 | VideoStudioCommand, | ||
14 | waitJobs | ||
15 | } from '@shared/server-commands' | ||
16 | |||
17 | describe('Test studio transcoding in peertube-runner program', function () { | ||
18 | let servers: PeerTubeServer[] = [] | ||
19 | let peertubeRunner: PeerTubeRunnerProcess | ||
20 | |||
21 | function runSuite (options: { | ||
22 | objectStorage: boolean | ||
23 | }) { | ||
24 | const { objectStorage } = options | ||
25 | |||
26 | it('Should run a complex studio transcoding', async function () { | ||
27 | this.timeout(120000) | ||
28 | |||
29 | const { uuid } = await servers[0].videos.quickUpload({ name: 'mp4', fixture: 'video_short.mp4' }) | ||
30 | await waitJobs(servers) | ||
31 | |||
32 | const video = await servers[0].videos.get({ id: uuid }) | ||
33 | const oldFileUrls = getAllFiles(video).map(f => f.fileUrl) | ||
34 | |||
35 | await servers[0].videoStudio.createEditionTasks({ videoId: uuid, tasks: VideoStudioCommand.getComplexTask() }) | ||
36 | await waitJobs(servers, { runnerJobs: true }) | ||
37 | |||
38 | for (const server of servers) { | ||
39 | const video = await server.videos.get({ id: uuid }) | ||
40 | const files = getAllFiles(video) | ||
41 | |||
42 | for (const f of files) { | ||
43 | expect(oldFileUrls).to.not.include(f.fileUrl) | ||
44 | } | ||
45 | |||
46 | if (objectStorage) { | ||
47 | for (const webtorrentFile of video.files) { | ||
48 | expectStartWith(webtorrentFile.fileUrl, ObjectStorageCommand.getMockWebTorrentBaseUrl()) | ||
49 | } | ||
50 | |||
51 | for (const hlsFile of video.streamingPlaylists[0].files) { | ||
52 | expectStartWith(hlsFile.fileUrl, ObjectStorageCommand.getMockPlaylistBaseUrl()) | ||
53 | } | ||
54 | } | ||
55 | |||
56 | await checkVideoDuration(server, uuid, 9) | ||
57 | } | ||
58 | }) | ||
59 | } | ||
60 | |||
61 | before(async function () { | ||
62 | this.timeout(120_000) | ||
63 | |||
64 | servers = await createMultipleServers(2) | ||
65 | |||
66 | await setAccessTokensToServers(servers) | ||
67 | await setDefaultVideoChannel(servers) | ||
68 | |||
69 | await doubleFollow(servers[0], servers[1]) | ||
70 | |||
71 | await servers[0].config.enableTranscoding(true, true) | ||
72 | await servers[0].config.enableStudio() | ||
73 | await servers[0].config.enableRemoteStudio() | ||
74 | |||
75 | const registrationToken = await servers[0].runnerRegistrationTokens.getFirstRegistrationToken() | ||
76 | |||
77 | peertubeRunner = new PeerTubeRunnerProcess() | ||
78 | await peertubeRunner.runServer({ hideLogs: false }) | ||
79 | await peertubeRunner.registerPeerTubeInstance({ server: servers[0], registrationToken, runnerName: 'runner' }) | ||
80 | }) | ||
81 | |||
82 | describe('With videos on local filesystem storage', function () { | ||
83 | runSuite({ objectStorage: false }) | ||
84 | }) | ||
85 | |||
86 | describe('With videos on object storage', function () { | ||
87 | if (areMockObjectStorageTestsDisabled()) return | ||
88 | |||
89 | before(async function () { | ||
90 | await ObjectStorageCommand.prepareDefaultMockBuckets() | ||
91 | |||
92 | await servers[0].kill() | ||
93 | |||
94 | await servers[0].run(ObjectStorageCommand.getDefaultMockConfig()) | ||
95 | |||
96 | // Wait for peertube runner socket reconnection | ||
97 | await wait(1500) | ||
98 | }) | ||
99 | |||
100 | runSuite({ objectStorage: true }) | ||
101 | }) | ||
102 | |||
103 | describe('Check cleanup', function () { | ||
104 | |||
105 | it('Should have an empty cache directory', async function () { | ||
106 | await checkPeerTubeRunnerCacheIsEmpty() | ||
107 | }) | ||
108 | }) | ||
109 | |||
110 | after(async function () { | ||
111 | await peertubeRunner.unregisterPeerTubeInstance({ server: servers[0] }) | ||
112 | peertubeRunner.kill() | ||
113 | |||
114 | await cleanupTests(servers) | ||
115 | }) | ||
116 | }) | ||
diff --git a/server/tests/peertube-runner/vod-transcoding.ts b/server/tests/peertube-runner/vod-transcoding.ts index bdf798379..3a9abba93 100644 --- a/server/tests/peertube-runner/vod-transcoding.ts +++ b/server/tests/peertube-runner/vod-transcoding.ts | |||
@@ -1,6 +1,11 @@ | |||
1 | /* 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 { expect } from 'chai' | 2 | import { expect } from 'chai' |
3 | import { completeCheckHlsPlaylist, completeWebVideoFilesCheck, PeerTubeRunnerProcess } from '@server/tests/shared' | 3 | import { |
4 | checkPeerTubeRunnerCacheIsEmpty, | ||
5 | completeCheckHlsPlaylist, | ||
6 | completeWebVideoFilesCheck, | ||
7 | PeerTubeRunnerProcess | ||
8 | } from '@server/tests/shared' | ||
4 | import { areMockObjectStorageTestsDisabled, getAllFiles, wait } from '@shared/core-utils' | 9 | import { areMockObjectStorageTestsDisabled, getAllFiles, wait } from '@shared/core-utils' |
5 | import { VideoPrivacy } from '@shared/models' | 10 | import { VideoPrivacy } from '@shared/models' |
6 | import { | 11 | import { |
@@ -321,6 +326,13 @@ describe('Test VOD transcoding in peertube-runner program', function () { | |||
321 | }) | 326 | }) |
322 | }) | 327 | }) |
323 | 328 | ||
329 | describe('Check cleanup', function () { | ||
330 | |||
331 | it('Should have an empty cache directory', async function () { | ||
332 | await checkPeerTubeRunnerCacheIsEmpty() | ||
333 | }) | ||
334 | }) | ||
335 | |||
324 | after(async function () { | 336 | after(async function () { |
325 | await peertubeRunner.unregisterPeerTubeInstance({ server: servers[0] }) | 337 | await peertubeRunner.unregisterPeerTubeInstance({ server: servers[0] }) |
326 | peertubeRunner.kill() | 338 | peertubeRunner.kill() |
diff --git a/server/tests/shared/checks.ts b/server/tests/shared/checks.ts index d7eb25bb5..feaef37c6 100644 --- a/server/tests/shared/checks.ts +++ b/server/tests/shared/checks.ts | |||
@@ -130,6 +130,22 @@ function checkBadSortPagination (url: string, path: string, token?: string, quer | |||
130 | }) | 130 | }) |
131 | } | 131 | } |
132 | 132 | ||
133 | // --------------------------------------------------------------------------- | ||
134 | |||
135 | async function checkVideoDuration (server: PeerTubeServer, videoUUID: string, duration: number) { | ||
136 | const video = await server.videos.get({ id: videoUUID }) | ||
137 | |||
138 | expect(video.duration).to.be.approximately(duration, 1) | ||
139 | |||
140 | for (const file of video.files) { | ||
141 | const metadata = await server.videos.getFileMetadata({ url: file.metadataUrl }) | ||
142 | |||
143 | for (const stream of metadata.streams) { | ||
144 | expect(Math.round(stream.duration)).to.be.approximately(duration, 1) | ||
145 | } | ||
146 | } | ||
147 | } | ||
148 | |||
133 | export { | 149 | export { |
134 | dateIsValid, | 150 | dateIsValid, |
135 | testImageSize, | 151 | testImageSize, |
@@ -142,5 +158,6 @@ export { | |||
142 | checkBadStartPagination, | 158 | checkBadStartPagination, |
143 | checkBadCountPagination, | 159 | checkBadCountPagination, |
144 | checkBadSortPagination, | 160 | checkBadSortPagination, |
161 | checkVideoDuration, | ||
145 | expectLogContain | 162 | expectLogContain |
146 | } | 163 | } |
diff --git a/server/tests/shared/directories.ts b/server/tests/shared/directories.ts index a614cef7c..4f4282554 100644 --- a/server/tests/shared/directories.ts +++ b/server/tests/shared/directories.ts | |||
@@ -2,9 +2,11 @@ | |||
2 | 2 | ||
3 | import { expect } from 'chai' | 3 | import { expect } from 'chai' |
4 | import { pathExists, readdir } from 'fs-extra' | 4 | import { pathExists, readdir } from 'fs-extra' |
5 | import { homedir } from 'os' | ||
6 | import { join } from 'path' | ||
5 | import { PeerTubeServer } from '@shared/server-commands' | 7 | import { PeerTubeServer } from '@shared/server-commands' |
6 | 8 | ||
7 | async function checkTmpIsEmpty (server: PeerTubeServer) { | 9 | export async function checkTmpIsEmpty (server: PeerTubeServer) { |
8 | await checkDirectoryIsEmpty(server, 'tmp', [ 'plugins-global.css', 'hls', 'resumable-uploads' ]) | 10 | await checkDirectoryIsEmpty(server, 'tmp', [ 'plugins-global.css', 'hls', 'resumable-uploads' ]) |
9 | 11 | ||
10 | if (await pathExists(server.getDirectoryPath('tmp/hls'))) { | 12 | if (await pathExists(server.getDirectoryPath('tmp/hls'))) { |
@@ -12,11 +14,11 @@ async function checkTmpIsEmpty (server: PeerTubeServer) { | |||
12 | } | 14 | } |
13 | } | 15 | } |
14 | 16 | ||
15 | async function checkPersistentTmpIsEmpty (server: PeerTubeServer) { | 17 | export async function checkPersistentTmpIsEmpty (server: PeerTubeServer) { |
16 | await checkDirectoryIsEmpty(server, 'tmp-persistent') | 18 | await checkDirectoryIsEmpty(server, 'tmp-persistent') |
17 | } | 19 | } |
18 | 20 | ||
19 | async function checkDirectoryIsEmpty (server: PeerTubeServer, directory: string, exceptions: string[] = []) { | 21 | export async function checkDirectoryIsEmpty (server: PeerTubeServer, directory: string, exceptions: string[] = []) { |
20 | const directoryPath = server.getDirectoryPath(directory) | 22 | const directoryPath = server.getDirectoryPath(directory) |
21 | 23 | ||
22 | const directoryExists = await pathExists(directoryPath) | 24 | const directoryExists = await pathExists(directoryPath) |
@@ -28,8 +30,13 @@ async function checkDirectoryIsEmpty (server: PeerTubeServer, directory: string, | |||
28 | expect(filtered).to.have.lengthOf(0) | 30 | expect(filtered).to.have.lengthOf(0) |
29 | } | 31 | } |
30 | 32 | ||
31 | export { | 33 | export async function checkPeerTubeRunnerCacheIsEmpty () { |
32 | checkTmpIsEmpty, | 34 | const directoryPath = join(homedir(), '.cache', 'peertube-runner-nodejs', 'test', 'transcoding') |
33 | checkPersistentTmpIsEmpty, | 35 | |
34 | checkDirectoryIsEmpty | 36 | const directoryExists = await pathExists(directoryPath) |
37 | expect(directoryExists).to.be.true | ||
38 | |||
39 | const files = await readdir(directoryPath) | ||
40 | |||
41 | expect(files).to.have.lengthOf(0) | ||
35 | } | 42 | } |