diff options
Diffstat (limited to 'packages/tests/src/api/check-params/video-studio.ts')
-rw-r--r-- | packages/tests/src/api/check-params/video-studio.ts | 392 |
1 files changed, 392 insertions, 0 deletions
diff --git a/packages/tests/src/api/check-params/video-studio.ts b/packages/tests/src/api/check-params/video-studio.ts new file mode 100644 index 000000000..ae83f3590 --- /dev/null +++ b/packages/tests/src/api/check-params/video-studio.ts | |||
@@ -0,0 +1,392 @@ | |||
1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ | ||
2 | |||
3 | import { HttpStatusCode, HttpStatusCodeType, VideoStudioTask } from '@peertube/peertube-models' | ||
4 | import { | ||
5 | cleanupTests, | ||
6 | createSingleServer, | ||
7 | PeerTubeServer, | ||
8 | setAccessTokensToServers, | ||
9 | VideoStudioCommand, | ||
10 | waitJobs | ||
11 | } from '@peertube/peertube-server-commands' | ||
12 | |||
13 | describe('Test video studio API validator', function () { | ||
14 | let server: PeerTubeServer | ||
15 | let command: VideoStudioCommand | ||
16 | let userAccessToken: string | ||
17 | let videoUUID: string | ||
18 | |||
19 | // --------------------------------------------------------------- | ||
20 | |||
21 | before(async function () { | ||
22 | this.timeout(120_000) | ||
23 | |||
24 | server = await createSingleServer(1) | ||
25 | |||
26 | await setAccessTokensToServers([ server ]) | ||
27 | userAccessToken = await server.users.generateUserAndToken('user1') | ||
28 | |||
29 | await server.config.enableMinimumTranscoding() | ||
30 | |||
31 | const { uuid } = await server.videos.quickUpload({ name: 'video' }) | ||
32 | videoUUID = uuid | ||
33 | |||
34 | command = server.videoStudio | ||
35 | |||
36 | await waitJobs([ server ]) | ||
37 | }) | ||
38 | |||
39 | describe('Task creation', function () { | ||
40 | |||
41 | describe('Config settings', function () { | ||
42 | |||
43 | it('Should fail if studio is disabled', async function () { | ||
44 | await server.config.updateExistingSubConfig({ | ||
45 | newConfig: { | ||
46 | videoStudio: { | ||
47 | enabled: false | ||
48 | } | ||
49 | } | ||
50 | }) | ||
51 | |||
52 | await command.createEditionTasks({ | ||
53 | videoId: videoUUID, | ||
54 | tasks: VideoStudioCommand.getComplexTask(), | ||
55 | expectedStatus: HttpStatusCode.BAD_REQUEST_400 | ||
56 | }) | ||
57 | }) | ||
58 | |||
59 | it('Should fail to enable studio if transcoding is disabled', async function () { | ||
60 | await server.config.updateExistingSubConfig({ | ||
61 | newConfig: { | ||
62 | videoStudio: { | ||
63 | enabled: true | ||
64 | }, | ||
65 | transcoding: { | ||
66 | enabled: false | ||
67 | } | ||
68 | }, | ||
69 | expectedStatus: HttpStatusCode.BAD_REQUEST_400 | ||
70 | }) | ||
71 | }) | ||
72 | |||
73 | it('Should succeed to enable video studio', async function () { | ||
74 | await server.config.updateExistingSubConfig({ | ||
75 | newConfig: { | ||
76 | videoStudio: { | ||
77 | enabled: true | ||
78 | }, | ||
79 | transcoding: { | ||
80 | enabled: true | ||
81 | } | ||
82 | } | ||
83 | }) | ||
84 | }) | ||
85 | }) | ||
86 | |||
87 | describe('Common tasks', function () { | ||
88 | |||
89 | it('Should fail without token', async function () { | ||
90 | await command.createEditionTasks({ | ||
91 | token: null, | ||
92 | videoId: videoUUID, | ||
93 | tasks: VideoStudioCommand.getComplexTask(), | ||
94 | expectedStatus: HttpStatusCode.UNAUTHORIZED_401 | ||
95 | }) | ||
96 | }) | ||
97 | |||
98 | it('Should fail with another user token', async function () { | ||
99 | await command.createEditionTasks({ | ||
100 | token: userAccessToken, | ||
101 | videoId: videoUUID, | ||
102 | tasks: VideoStudioCommand.getComplexTask(), | ||
103 | expectedStatus: HttpStatusCode.FORBIDDEN_403 | ||
104 | }) | ||
105 | }) | ||
106 | |||
107 | it('Should fail with an invalid video', async function () { | ||
108 | await command.createEditionTasks({ | ||
109 | videoId: 'tintin', | ||
110 | tasks: VideoStudioCommand.getComplexTask(), | ||
111 | expectedStatus: HttpStatusCode.BAD_REQUEST_400 | ||
112 | }) | ||
113 | }) | ||
114 | |||
115 | it('Should fail with an unknown video', async function () { | ||
116 | await command.createEditionTasks({ | ||
117 | videoId: 42, | ||
118 | tasks: VideoStudioCommand.getComplexTask(), | ||
119 | expectedStatus: HttpStatusCode.NOT_FOUND_404 | ||
120 | }) | ||
121 | }) | ||
122 | |||
123 | it('Should fail with an already in transcoding state video', async function () { | ||
124 | this.timeout(60000) | ||
125 | |||
126 | const { uuid } = await server.videos.quickUpload({ name: 'transcoded video' }) | ||
127 | await waitJobs([ server ]) | ||
128 | |||
129 | await server.jobs.pauseJobQueue() | ||
130 | await server.videos.runTranscoding({ videoId: uuid, transcodingType: 'hls' }) | ||
131 | |||
132 | await command.createEditionTasks({ | ||
133 | videoId: uuid, | ||
134 | tasks: VideoStudioCommand.getComplexTask(), | ||
135 | expectedStatus: HttpStatusCode.CONFLICT_409 | ||
136 | }) | ||
137 | |||
138 | await server.jobs.resumeJobQueue() | ||
139 | }) | ||
140 | |||
141 | it('Should fail with a bad complex task', async function () { | ||
142 | await command.createEditionTasks({ | ||
143 | videoId: videoUUID, | ||
144 | tasks: [ | ||
145 | { | ||
146 | name: 'cut', | ||
147 | options: { | ||
148 | start: 1, | ||
149 | end: 2 | ||
150 | } | ||
151 | }, | ||
152 | { | ||
153 | name: 'hadock', | ||
154 | options: { | ||
155 | start: 1, | ||
156 | end: 2 | ||
157 | } | ||
158 | } | ||
159 | ] as any, | ||
160 | expectedStatus: HttpStatusCode.BAD_REQUEST_400 | ||
161 | }) | ||
162 | }) | ||
163 | |||
164 | it('Should fail without task', async function () { | ||
165 | await command.createEditionTasks({ | ||
166 | videoId: videoUUID, | ||
167 | tasks: [], | ||
168 | expectedStatus: HttpStatusCode.BAD_REQUEST_400 | ||
169 | }) | ||
170 | }) | ||
171 | |||
172 | it('Should fail with too many tasks', async function () { | ||
173 | const tasks: VideoStudioTask[] = [] | ||
174 | |||
175 | for (let i = 0; i < 110; i++) { | ||
176 | tasks.push({ | ||
177 | name: 'cut', | ||
178 | options: { | ||
179 | start: 1 | ||
180 | } | ||
181 | }) | ||
182 | } | ||
183 | |||
184 | await command.createEditionTasks({ | ||
185 | videoId: videoUUID, | ||
186 | tasks, | ||
187 | expectedStatus: HttpStatusCode.BAD_REQUEST_400 | ||
188 | }) | ||
189 | }) | ||
190 | |||
191 | it('Should succeed with correct parameters', async function () { | ||
192 | await server.jobs.pauseJobQueue() | ||
193 | |||
194 | await command.createEditionTasks({ | ||
195 | videoId: videoUUID, | ||
196 | tasks: VideoStudioCommand.getComplexTask(), | ||
197 | expectedStatus: HttpStatusCode.NO_CONTENT_204 | ||
198 | }) | ||
199 | }) | ||
200 | |||
201 | it('Should fail with a video that is already waiting for edition', async function () { | ||
202 | this.timeout(120000) | ||
203 | |||
204 | await command.createEditionTasks({ | ||
205 | videoId: videoUUID, | ||
206 | tasks: VideoStudioCommand.getComplexTask(), | ||
207 | expectedStatus: HttpStatusCode.CONFLICT_409 | ||
208 | }) | ||
209 | |||
210 | await server.jobs.resumeJobQueue() | ||
211 | |||
212 | await waitJobs([ server ]) | ||
213 | }) | ||
214 | }) | ||
215 | |||
216 | describe('Cut task', function () { | ||
217 | |||
218 | async function cut (start: number, end: number, expectedStatus: HttpStatusCodeType = HttpStatusCode.BAD_REQUEST_400) { | ||
219 | await command.createEditionTasks({ | ||
220 | videoId: videoUUID, | ||
221 | tasks: [ | ||
222 | { | ||
223 | name: 'cut', | ||
224 | options: { | ||
225 | start, | ||
226 | end | ||
227 | } | ||
228 | } | ||
229 | ], | ||
230 | expectedStatus | ||
231 | }) | ||
232 | } | ||
233 | |||
234 | it('Should fail with bad start/end', async function () { | ||
235 | const invalid = [ | ||
236 | 'tintin', | ||
237 | -1, | ||
238 | undefined | ||
239 | ] | ||
240 | |||
241 | for (const value of invalid) { | ||
242 | await cut(value as any, undefined) | ||
243 | await cut(undefined, value as any) | ||
244 | } | ||
245 | }) | ||
246 | |||
247 | it('Should fail with the same start/end', async function () { | ||
248 | await cut(2, 2) | ||
249 | }) | ||
250 | |||
251 | it('Should fail with inconsistents start/end', async function () { | ||
252 | await cut(2, 1) | ||
253 | }) | ||
254 | |||
255 | it('Should fail without start and end', async function () { | ||
256 | await cut(undefined, undefined) | ||
257 | }) | ||
258 | |||
259 | it('Should succeed with the correct params', async function () { | ||
260 | this.timeout(120000) | ||
261 | |||
262 | await cut(0, 2, HttpStatusCode.NO_CONTENT_204) | ||
263 | |||
264 | await waitJobs([ server ]) | ||
265 | }) | ||
266 | }) | ||
267 | |||
268 | describe('Watermark task', function () { | ||
269 | |||
270 | async function addWatermark (file: string, expectedStatus: HttpStatusCodeType = HttpStatusCode.BAD_REQUEST_400) { | ||
271 | await command.createEditionTasks({ | ||
272 | videoId: videoUUID, | ||
273 | tasks: [ | ||
274 | { | ||
275 | name: 'add-watermark', | ||
276 | options: { | ||
277 | file | ||
278 | } | ||
279 | } | ||
280 | ], | ||
281 | expectedStatus | ||
282 | }) | ||
283 | } | ||
284 | |||
285 | it('Should fail without waterkmark', async function () { | ||
286 | await addWatermark(undefined) | ||
287 | }) | ||
288 | |||
289 | it('Should fail with an invalid watermark', async function () { | ||
290 | await addWatermark('video_short.mp4') | ||
291 | }) | ||
292 | |||
293 | it('Should succeed with the correct params', async function () { | ||
294 | this.timeout(120000) | ||
295 | |||
296 | await addWatermark('custom-thumbnail.jpg', HttpStatusCode.NO_CONTENT_204) | ||
297 | |||
298 | await waitJobs([ server ]) | ||
299 | }) | ||
300 | }) | ||
301 | |||
302 | describe('Intro/Outro task', function () { | ||
303 | |||
304 | async function addIntroOutro ( | ||
305 | type: 'add-intro' | 'add-outro', | ||
306 | file: string, | ||
307 | expectedStatus: HttpStatusCodeType = HttpStatusCode.BAD_REQUEST_400 | ||
308 | ) { | ||
309 | await command.createEditionTasks({ | ||
310 | videoId: videoUUID, | ||
311 | tasks: [ | ||
312 | { | ||
313 | name: type, | ||
314 | options: { | ||
315 | file | ||
316 | } | ||
317 | } | ||
318 | ], | ||
319 | expectedStatus | ||
320 | }) | ||
321 | } | ||
322 | |||
323 | it('Should fail without file', async function () { | ||
324 | await addIntroOutro('add-intro', undefined) | ||
325 | await addIntroOutro('add-outro', undefined) | ||
326 | }) | ||
327 | |||
328 | it('Should fail with an invalid file', async function () { | ||
329 | await addIntroOutro('add-intro', 'custom-thumbnail.jpg') | ||
330 | await addIntroOutro('add-outro', 'custom-thumbnail.jpg') | ||
331 | }) | ||
332 | |||
333 | it('Should fail with a file that does not contain video stream', async function () { | ||
334 | await addIntroOutro('add-intro', 'sample.ogg') | ||
335 | await addIntroOutro('add-outro', 'sample.ogg') | ||
336 | |||
337 | }) | ||
338 | |||
339 | it('Should succeed with the correct params', async function () { | ||
340 | this.timeout(120000) | ||
341 | |||
342 | await addIntroOutro('add-intro', 'video_very_short_240p.mp4', HttpStatusCode.NO_CONTENT_204) | ||
343 | await waitJobs([ server ]) | ||
344 | |||
345 | await addIntroOutro('add-outro', 'video_very_short_240p.mp4', HttpStatusCode.NO_CONTENT_204) | ||
346 | await waitJobs([ server ]) | ||
347 | }) | ||
348 | |||
349 | it('Should check total quota when creating the task', async function () { | ||
350 | this.timeout(120000) | ||
351 | |||
352 | const user = await server.users.create({ username: 'user_quota_1' }) | ||
353 | const token = await server.login.getAccessToken('user_quota_1') | ||
354 | const { uuid } = await server.videos.quickUpload({ token, name: 'video_quota_1', fixture: 'video_short.mp4' }) | ||
355 | |||
356 | const addIntroOutroByUser = (type: 'add-intro' | 'add-outro', expectedStatus: HttpStatusCodeType) => { | ||
357 | return command.createEditionTasks({ | ||
358 | token, | ||
359 | videoId: uuid, | ||
360 | tasks: [ | ||
361 | { | ||
362 | name: type, | ||
363 | options: { | ||
364 | file: 'video_short.mp4' | ||
365 | } | ||
366 | } | ||
367 | ], | ||
368 | expectedStatus | ||
369 | }) | ||
370 | } | ||
371 | |||
372 | await waitJobs([ server ]) | ||
373 | |||
374 | const { videoQuotaUsed } = await server.users.getMyQuotaUsed({ token }) | ||
375 | await server.users.update({ userId: user.id, videoQuota: Math.round(videoQuotaUsed * 2.5) }) | ||
376 | |||
377 | // Still valid | ||
378 | await addIntroOutroByUser('add-intro', HttpStatusCode.NO_CONTENT_204) | ||
379 | |||
380 | await waitJobs([ server ]) | ||
381 | |||
382 | // Too much quota | ||
383 | await addIntroOutroByUser('add-intro', HttpStatusCode.PAYLOAD_TOO_LARGE_413) | ||
384 | await addIntroOutroByUser('add-outro', HttpStatusCode.PAYLOAD_TOO_LARGE_413) | ||
385 | }) | ||
386 | }) | ||
387 | }) | ||
388 | |||
389 | after(async function () { | ||
390 | await cleanupTests([ server ]) | ||
391 | }) | ||
392 | }) | ||