]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/tests/api/check-params/video-studio.ts
Bumped to version v5.2.1
[github/Chocobozzz/PeerTube.git] / server / tests / api / check-params / video-studio.ts
1 /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3 import { HttpStatusCode, VideoStudioTask } from '@shared/models'
4 import {
5 cleanupTests,
6 createSingleServer,
7 PeerTubeServer,
8 setAccessTokensToServers,
9 VideoStudioCommand,
10 waitJobs
11 } from '@shared/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 = 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 = 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('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 (type: 'add-intro' | 'add-outro', file: string, expectedStatus = HttpStatusCode.BAD_REQUEST_400) {
305 await command.createEditionTasks({
306 videoId: videoUUID,
307 tasks: [
308 {
309 name: type,
310 options: {
311 file
312 }
313 }
314 ],
315 expectedStatus
316 })
317 }
318
319 it('Should fail without file', async function () {
320 await addIntroOutro('add-intro', undefined)
321 await addIntroOutro('add-outro', undefined)
322 })
323
324 it('Should fail with an invalid file', async function () {
325 await addIntroOutro('add-intro', 'thumbnail.jpg')
326 await addIntroOutro('add-outro', 'thumbnail.jpg')
327 })
328
329 it('Should fail with a file that does not contain video stream', async function () {
330 await addIntroOutro('add-intro', 'sample.ogg')
331 await addIntroOutro('add-outro', 'sample.ogg')
332
333 })
334
335 it('Should succeed with the correct params', async function () {
336 this.timeout(120000)
337
338 await addIntroOutro('add-intro', 'video_very_short_240p.mp4', HttpStatusCode.NO_CONTENT_204)
339 await waitJobs([ server ])
340
341 await addIntroOutro('add-outro', 'video_very_short_240p.mp4', HttpStatusCode.NO_CONTENT_204)
342 await waitJobs([ server ])
343 })
344
345 it('Should check total quota when creating the task', async function () {
346 this.timeout(120000)
347
348 const user = await server.users.create({ username: 'user_quota_1' })
349 const token = await server.login.getAccessToken('user_quota_1')
350 const { uuid } = await server.videos.quickUpload({ token, name: 'video_quota_1', fixture: 'video_short.mp4' })
351
352 const addIntroOutroByUser = (type: 'add-intro' | 'add-outro', expectedStatus: HttpStatusCode) => {
353 return command.createEditionTasks({
354 token,
355 videoId: uuid,
356 tasks: [
357 {
358 name: type,
359 options: {
360 file: 'video_short.mp4'
361 }
362 }
363 ],
364 expectedStatus
365 })
366 }
367
368 await waitJobs([ server ])
369
370 const { videoQuotaUsed } = await server.users.getMyQuotaUsed({ token })
371 await server.users.update({ userId: user.id, videoQuota: Math.round(videoQuotaUsed * 2.5) })
372
373 // Still valid
374 await addIntroOutroByUser('add-intro', HttpStatusCode.NO_CONTENT_204)
375
376 await waitJobs([ server ])
377
378 // Too much quota
379 await addIntroOutroByUser('add-intro', HttpStatusCode.PAYLOAD_TOO_LARGE_413)
380 await addIntroOutroByUser('add-outro', HttpStatusCode.PAYLOAD_TOO_LARGE_413)
381 })
382 })
383 })
384
385 after(async function () {
386 await cleanupTests([ server ])
387 })
388 })