Server: Bulk update videos support field
[oweals/peertube.git] / server / tests / api / check-params / video-playlists.ts
1 /* tslint:disable:no-unused-expression */
2
3 import 'mocha'
4 import {
5   addVideoInPlaylist,
6   cleanupTests,
7   createVideoPlaylist,
8   deleteVideoPlaylist,
9   flushAndRunServer,
10   generateUserAccessToken,
11   getAccountPlaylistsListWithToken,
12   getVideoPlaylist,
13   immutableAssign,
14   makeGetRequest,
15   removeVideoFromPlaylist,
16   reorderVideosPlaylist,
17   ServerInfo,
18   setAccessTokensToServers,
19   setDefaultVideoChannel,
20   updateVideoPlaylist,
21   updateVideoPlaylistElement,
22   uploadVideoAndGetId
23 } from '../../../../shared/extra-utils'
24 import {
25   checkBadCountPagination,
26   checkBadSortPagination,
27   checkBadStartPagination
28 } from '../../../../shared/extra-utils/requests/check-api-params'
29 import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model'
30 import { VideoPlaylistType } from '../../../../shared/models/videos/playlist/video-playlist-type.model'
31
32 describe('Test video playlists API validator', function () {
33   let server: ServerInfo
34   let userAccessToken: string
35   let playlistUUID: string
36   let privatePlaylistUUID: string
37   let watchLaterPlaylistId: number
38   let videoId: number
39   let videoId2: number
40
41   // ---------------------------------------------------------------
42
43   before(async function () {
44     this.timeout(30000)
45
46     server = await flushAndRunServer(1)
47
48     await setAccessTokensToServers([ server ])
49     await setDefaultVideoChannel([ server ])
50
51     userAccessToken = await generateUserAccessToken(server, 'user1')
52     videoId = (await uploadVideoAndGetId({ server, videoName: 'video 1' })).id
53     videoId2 = (await uploadVideoAndGetId({ server, videoName: 'video 2' })).id
54
55     {
56       const res = await getAccountPlaylistsListWithToken(server.url, server.accessToken, 'root',0, 5, VideoPlaylistType.WATCH_LATER)
57       watchLaterPlaylistId = res.body.data[0].id
58     }
59
60     {
61       const res = await createVideoPlaylist({
62         url: server.url,
63         token: server.accessToken,
64         playlistAttrs: {
65           displayName: 'super playlist',
66           privacy: VideoPlaylistPrivacy.PUBLIC,
67           videoChannelId: server.videoChannel.id
68         }
69       })
70       playlistUUID = res.body.videoPlaylist.uuid
71     }
72
73     {
74       const res = await createVideoPlaylist({
75         url: server.url,
76         token: server.accessToken,
77         playlistAttrs: {
78           displayName: 'private',
79           privacy: VideoPlaylistPrivacy.PRIVATE
80         }
81       })
82       privatePlaylistUUID = res.body.videoPlaylist.uuid
83     }
84   })
85
86   describe('When listing playlists', function () {
87     const globalPath = '/api/v1/video-playlists'
88     const accountPath = '/api/v1/accounts/root/video-playlists'
89     const videoChannelPath = '/api/v1/video-channels/root_channel/video-playlists'
90
91     it('Should fail with a bad start pagination', async function () {
92       await checkBadStartPagination(server.url, globalPath, server.accessToken)
93       await checkBadStartPagination(server.url, accountPath, server.accessToken)
94       await checkBadStartPagination(server.url, videoChannelPath, server.accessToken)
95     })
96
97     it('Should fail with a bad count pagination', async function () {
98       await checkBadCountPagination(server.url, globalPath, server.accessToken)
99       await checkBadCountPagination(server.url, accountPath, server.accessToken)
100       await checkBadCountPagination(server.url, videoChannelPath, server.accessToken)
101     })
102
103     it('Should fail with an incorrect sort', async function () {
104       await checkBadSortPagination(server.url, globalPath, server.accessToken)
105       await checkBadSortPagination(server.url, accountPath, server.accessToken)
106       await checkBadSortPagination(server.url, videoChannelPath, server.accessToken)
107     })
108
109     it('Should fail with a bad playlist type', async function () {
110       await makeGetRequest({ url: server.url, path: globalPath, query: { playlistType: 3 } })
111       await makeGetRequest({ url: server.url, path: accountPath, query: { playlistType: 3 } })
112       await makeGetRequest({ url: server.url, path: videoChannelPath, query: { playlistType: 3 } })
113     })
114
115     it('Should fail with a bad account parameter', async function () {
116       const accountPath = '/api/v1/accounts/root2/video-playlists'
117
118       await makeGetRequest({ url: server.url, path: accountPath, statusCodeExpected: 404, token: server.accessToken })
119     })
120
121     it('Should fail with a bad video channel parameter', async function () {
122       const accountPath = '/api/v1/video-channels/bad_channel/video-playlists'
123
124       await makeGetRequest({ url: server.url, path: accountPath, statusCodeExpected: 404, token: server.accessToken })
125     })
126
127     it('Should success with the correct parameters', async function () {
128       await makeGetRequest({ url: server.url, path: globalPath, statusCodeExpected: 200, token: server.accessToken })
129       await makeGetRequest({ url: server.url, path: accountPath, statusCodeExpected: 200, token: server.accessToken })
130       await makeGetRequest({ url: server.url, path: videoChannelPath, statusCodeExpected: 200, token: server.accessToken })
131     })
132   })
133
134   describe('When listing videos of a playlist', function () {
135     const path = '/api/v1/video-playlists'
136
137     it('Should fail with a bad start pagination', async function () {
138       await checkBadStartPagination(server.url, path, server.accessToken)
139     })
140
141     it('Should fail with a bad count pagination', async function () {
142       await checkBadCountPagination(server.url, path, server.accessToken)
143     })
144
145     it('Should fail with a bad filter', async function () {
146       await checkBadSortPagination(server.url, path, server.accessToken)
147     })
148   })
149
150   describe('When getting a video playlist', function () {
151     it('Should fail with a bad id or uuid', async function () {
152       await getVideoPlaylist(server.url, 'toto', 400)
153     })
154
155     it('Should fail with an unknown playlist', async function () {
156       await getVideoPlaylist(server.url, 42, 404)
157     })
158
159     it('Should fail to get an unlisted playlist with the number id', async function () {
160       const res = await createVideoPlaylist({
161         url: server.url,
162         token: server.accessToken,
163         playlistAttrs: {
164           displayName: 'super playlist',
165           privacy: VideoPlaylistPrivacy.UNLISTED
166         }
167       })
168       const playlist = res.body.videoPlaylist
169
170       await getVideoPlaylist(server.url, playlist.id, 404)
171       await getVideoPlaylist(server.url, playlist.uuid, 200)
172     })
173
174     it('Should succeed with the correct params', async function () {
175       await getVideoPlaylist(server.url, playlistUUID, 200)
176     })
177   })
178
179   describe('When creating/updating a video playlist', function () {
180     const getBase = (playlistAttrs: any = {}, wrapper: any = {}) => {
181       return Object.assign({
182         expectedStatus: 400,
183         url: server.url,
184         token: server.accessToken,
185         playlistAttrs: Object.assign({
186           displayName: 'display name',
187           privacy: VideoPlaylistPrivacy.UNLISTED,
188           thumbnailfile: 'thumbnail.jpg',
189           videoChannelId: server.videoChannel.id
190         }, playlistAttrs)
191       }, wrapper)
192     }
193     const getUpdate = (params: any, playlistId: number | string) => {
194       return immutableAssign(params, { playlistId: playlistId })
195     }
196
197     it('Should fail with an unauthenticated user', async function () {
198       const params = getBase({}, { token: null, expectedStatus: 401 })
199
200       await createVideoPlaylist(params)
201       await updateVideoPlaylist(getUpdate(params, playlistUUID))
202     })
203
204     it('Should fail without displayName', async function () {
205       const params = getBase({ displayName: undefined })
206
207       await createVideoPlaylist(params)
208       await updateVideoPlaylist(getUpdate(params, playlistUUID))
209     })
210
211     it('Should fail with an incorrect display name', async function () {
212       const params = getBase({ displayName: 's'.repeat(300) })
213
214       await createVideoPlaylist(params)
215       await updateVideoPlaylist(getUpdate(params, playlistUUID))
216     })
217
218     it('Should fail with an incorrect description', async function () {
219       const params = getBase({ description: 't' })
220
221       await createVideoPlaylist(params)
222       await updateVideoPlaylist(getUpdate(params, playlistUUID))
223     })
224
225     it('Should fail with an incorrect privacy', async function () {
226       const params = getBase({ privacy: 45 })
227
228       await createVideoPlaylist(params)
229       await updateVideoPlaylist(getUpdate(params, playlistUUID))
230     })
231
232     it('Should fail with an unknown video channel id', async function () {
233       const params = getBase({ videoChannelId: 42 }, { expectedStatus: 404 })
234
235       await createVideoPlaylist(params)
236       await updateVideoPlaylist(getUpdate(params, playlistUUID))
237     })
238
239     it('Should fail with an incorrect thumbnail file', async function () {
240       const params = getBase({ thumbnailfile: 'avatar.png' })
241
242       await createVideoPlaylist(params)
243       await updateVideoPlaylist(getUpdate(params, playlistUUID))
244     })
245
246     it('Should fail to set "public" a playlist not assigned to a channel', async function () {
247       const params = getBase({ privacy: VideoPlaylistPrivacy.PUBLIC, videoChannelId: undefined })
248       const params2 = getBase({ privacy: VideoPlaylistPrivacy.PUBLIC, videoChannelId: 'null' })
249       const params3 = getBase({ privacy: undefined, videoChannelId: 'null' })
250
251       await createVideoPlaylist(params)
252       await createVideoPlaylist(params2)
253       await updateVideoPlaylist(getUpdate(params, privatePlaylistUUID))
254       await updateVideoPlaylist(getUpdate(params2, playlistUUID))
255       await updateVideoPlaylist(getUpdate(params3, playlistUUID))
256     })
257
258     it('Should fail with an unknown playlist to update', async function () {
259       await updateVideoPlaylist(getUpdate(
260         getBase({}, { expectedStatus: 404 }),
261         42
262       ))
263     })
264
265     it('Should fail to update a playlist of another user', async function () {
266       await updateVideoPlaylist(getUpdate(
267         getBase({}, { token: userAccessToken, expectedStatus: 403 }),
268         playlistUUID
269       ))
270     })
271
272     it('Should fail to update to private a public/unlisted playlist', async function () {
273       const params = getBase({ privacy: VideoPlaylistPrivacy.PUBLIC }, { expectedStatus: 200 })
274
275       const res = await createVideoPlaylist(params)
276       const playlist = res.body.videoPlaylist
277
278       const paramsUpdate = getBase({ privacy: VideoPlaylistPrivacy.PRIVATE }, { expectedStatus: 400 })
279
280       await updateVideoPlaylist(getUpdate(paramsUpdate, playlist.id))
281     })
282
283     it('Should fail to update the watch later playlist', async function () {
284       await updateVideoPlaylist(getUpdate(
285         getBase({}, { expectedStatus: 400 }),
286         watchLaterPlaylistId
287       ))
288     })
289
290     it('Should succeed with the correct params', async function () {
291       {
292         const params = getBase({}, { expectedStatus: 200 })
293         await createVideoPlaylist(params)
294       }
295
296       {
297         const params = getBase({}, { expectedStatus: 204 })
298         await updateVideoPlaylist(getUpdate(params, playlistUUID))
299       }
300     })
301   })
302
303   describe('When adding an element in a playlist', function () {
304     const getBase = (elementAttrs: any = {}, wrapper: any = {}) => {
305       return Object.assign({
306         expectedStatus: 400,
307         url: server.url,
308         token: server.accessToken,
309         playlistId: playlistUUID,
310         elementAttrs: Object.assign({
311           videoId: videoId,
312           startTimestamp: 2,
313           stopTimestamp: 3
314         }, elementAttrs)
315       }, wrapper)
316     }
317
318     it('Should fail with an unauthenticated user', async function () {
319       const params = getBase({}, { token: null, expectedStatus: 401 })
320       await addVideoInPlaylist(params)
321     })
322
323     it('Should fail with the playlist of another user', async function () {
324       const params = getBase({}, { token: userAccessToken, expectedStatus: 403 })
325       await addVideoInPlaylist(params)
326     })
327
328     it('Should fail with an unknown or incorrect playlist id', async function () {
329       {
330         const params = getBase({}, { playlistId: 'toto' })
331         await addVideoInPlaylist(params)
332       }
333
334       {
335         const params = getBase({}, { playlistId: 42, expectedStatus: 404 })
336         await addVideoInPlaylist(params)
337       }
338     })
339
340     it('Should fail with an unknown or incorrect video id', async function () {
341       const params = getBase({ videoId: 42 }, { expectedStatus: 404 })
342       await addVideoInPlaylist(params)
343     })
344
345     it('Should fail with a bad start/stop timestamp', async function () {
346       {
347         const params = getBase({ startTimestamp: -42 })
348         await addVideoInPlaylist(params)
349       }
350
351       {
352         const params = getBase({ stopTimestamp: 'toto' as any })
353         await addVideoInPlaylist(params)
354       }
355     })
356
357     it('Succeed with the correct params', async function () {
358       const params = getBase({}, { expectedStatus: 200 })
359       await addVideoInPlaylist(params)
360     })
361
362     it('Should fail if the video was already added in the playlist', async function () {
363       const params = getBase({}, { expectedStatus: 409 })
364       await addVideoInPlaylist(params)
365     })
366   })
367
368   describe('When updating an element in a playlist', function () {
369     const getBase = (elementAttrs: any = {}, wrapper: any = {}) => {
370       return Object.assign({
371         url: server.url,
372         token: server.accessToken,
373         elementAttrs: Object.assign({
374           startTimestamp: 1,
375           stopTimestamp: 2
376         }, elementAttrs),
377         videoId: videoId,
378         playlistId: playlistUUID,
379         expectedStatus: 400
380       }, wrapper)
381     }
382
383     it('Should fail with an unauthenticated user', async function () {
384       const params = getBase({}, { token: null, expectedStatus: 401 })
385       await updateVideoPlaylistElement(params)
386     })
387
388     it('Should fail with the playlist of another user', async function () {
389       const params = getBase({}, { token: userAccessToken, expectedStatus: 403 })
390       await updateVideoPlaylistElement(params)
391     })
392
393     it('Should fail with an unknown or incorrect playlist id', async function () {
394       {
395         const params = getBase({}, { playlistId: 'toto' })
396         await updateVideoPlaylistElement(params)
397       }
398
399       {
400         const params = getBase({}, { playlistId: 42, expectedStatus: 404 })
401         await updateVideoPlaylistElement(params)
402       }
403     })
404
405     it('Should fail with an unknown or incorrect video id', async function () {
406       {
407         const params = getBase({}, { videoId: 'toto' })
408         await updateVideoPlaylistElement(params)
409       }
410
411       {
412         const params = getBase({}, { videoId: 42, expectedStatus: 404 })
413         await updateVideoPlaylistElement(params)
414       }
415     })
416
417     it('Should fail with a bad start/stop timestamp', async function () {
418       {
419         const params = getBase({ startTimestamp: 'toto' as any })
420         await updateVideoPlaylistElement(params)
421       }
422
423       {
424         const params = getBase({ stopTimestamp: -42 })
425         await updateVideoPlaylistElement(params)
426       }
427     })
428
429     it('Should fail with an unknown element', async function () {
430       const params = getBase({}, { videoId: videoId2, expectedStatus: 404 })
431       await updateVideoPlaylistElement(params)
432     })
433
434     it('Succeed with the correct params', async function () {
435       const params = getBase({}, { expectedStatus: 204 })
436       await updateVideoPlaylistElement(params)
437     })
438   })
439
440   describe('When reordering elements of a playlist', function () {
441     let videoId3: number
442     let videoId4: number
443
444     const getBase = (elementAttrs: any = {}, wrapper: any = {}) => {
445       return Object.assign({
446         url: server.url,
447         token: server.accessToken,
448         playlistId: playlistUUID,
449         elementAttrs: Object.assign({
450           startPosition: 1,
451           insertAfterPosition: 2,
452           reorderLength: 3
453         }, elementAttrs),
454         expectedStatus: 400
455       }, wrapper)
456     }
457
458     before(async function () {
459       videoId3 = (await uploadVideoAndGetId({ server, videoName: 'video 3' })).id
460       videoId4 = (await uploadVideoAndGetId({ server, videoName: 'video 4' })).id
461
462       for (let id of [ videoId3, videoId4 ]) {
463         await addVideoInPlaylist({
464           url: server.url,
465           token: server.accessToken,
466           playlistId: playlistUUID,
467           elementAttrs: { videoId: id }
468         })
469       }
470     })
471
472     it('Should fail with an unauthenticated user', async function () {
473       const params = getBase({}, { token: null, expectedStatus: 401 })
474       await reorderVideosPlaylist(params)
475     })
476
477     it('Should fail with the playlist of another user', async function () {
478       const params = getBase({}, { token: userAccessToken, expectedStatus: 403 })
479       await reorderVideosPlaylist(params)
480     })
481
482     it('Should fail with an invalid playlist', async function () {
483       {
484         const params = getBase({}, { playlistId: 'toto' })
485         await reorderVideosPlaylist(params)
486       }
487
488       {
489         const params = getBase({}, {  playlistId: 42, expectedStatus: 404 })
490         await reorderVideosPlaylist(params)
491       }
492     })
493
494     it('Should fail with an invalid start position', async function () {
495       {
496         const params = getBase({ startPosition: -1 })
497         await reorderVideosPlaylist(params)
498       }
499
500       {
501         const params = getBase({ startPosition: 'toto' as any })
502         await reorderVideosPlaylist(params)
503       }
504
505       {
506         const params = getBase({ startPosition: 42 })
507         await reorderVideosPlaylist(params)
508       }
509     })
510
511     it('Should fail with an invalid insert after position', async function () {
512       {
513         const params = getBase({ insertAfterPosition: 'toto' as any })
514         await reorderVideosPlaylist(params)
515       }
516
517       {
518         const params = getBase({ insertAfterPosition: -2 })
519         await reorderVideosPlaylist(params)
520       }
521
522       {
523         const params = getBase({ insertAfterPosition: 42 })
524         await reorderVideosPlaylist(params)
525       }
526     })
527
528     it('Should fail with an invalid reorder length', async function () {
529       {
530         const params = getBase({ reorderLength: 'toto' as any })
531         await reorderVideosPlaylist(params)
532       }
533
534       {
535         const params = getBase({ reorderLength: -2 })
536         await reorderVideosPlaylist(params)
537       }
538
539       {
540         const params = getBase({ reorderLength: 42 })
541         await reorderVideosPlaylist(params)
542       }
543     })
544
545     it('Succeed with the correct params', async function () {
546       const params = getBase({}, { expectedStatus: 204 })
547       await reorderVideosPlaylist(params)
548     })
549   })
550
551   describe('When checking exists in playlist endpoint', function () {
552     const path = '/api/v1/users/me/video-playlists/videos-exist'
553
554     it('Should fail with an unauthenticated user', async function () {
555       await makeGetRequest({
556         url: server.url,
557         path,
558         query: { videoIds: [ 1, 2 ] },
559         statusCodeExpected: 401
560       })
561     })
562
563     it('Should fail with invalid video ids', async function () {
564       await makeGetRequest({
565         url: server.url,
566         token: server.accessToken,
567         path,
568         query: { videoIds: 'toto' }
569       })
570
571       await makeGetRequest({
572         url: server.url,
573         token: server.accessToken,
574         path,
575         query: { videoIds: [ 'toto' ] }
576       })
577
578       await makeGetRequest({
579         url: server.url,
580         token: server.accessToken,
581         path,
582         query: { videoIds: [ 1, 'toto' ] }
583       })
584     })
585
586     it('Should succeed with the correct params', async function () {
587       await makeGetRequest({
588         url: server.url,
589         token: server.accessToken,
590         path,
591         query: { videoIds: [ 1, 2 ] },
592         statusCodeExpected: 200
593       })
594     })
595   })
596
597   describe('When deleting an element in a playlist', function () {
598     const getBase = (wrapper: any = {}) => {
599       return Object.assign({
600         url: server.url,
601         token: server.accessToken,
602         videoId: videoId,
603         playlistId: playlistUUID,
604         expectedStatus: 400
605       }, wrapper)
606     }
607
608     it('Should fail with an unauthenticated user', async function () {
609       const params = getBase({ token: null, expectedStatus: 401 })
610       await removeVideoFromPlaylist(params)
611     })
612
613     it('Should fail with the playlist of another user', async function () {
614       const params = getBase({ token: userAccessToken, expectedStatus: 403 })
615       await removeVideoFromPlaylist(params)
616     })
617
618     it('Should fail with an unknown or incorrect playlist id', async function () {
619       {
620         const params = getBase({ playlistId: 'toto' })
621         await removeVideoFromPlaylist(params)
622       }
623
624       {
625         const params = getBase({ playlistId: 42, expectedStatus: 404 })
626         await removeVideoFromPlaylist(params)
627       }
628     })
629
630     it('Should fail with an unknown or incorrect video id', async function () {
631       {
632         const params = getBase({ videoId: 'toto' })
633         await removeVideoFromPlaylist(params)
634       }
635
636       {
637         const params = getBase({ videoId: 42, expectedStatus: 404 })
638         await removeVideoFromPlaylist(params)
639       }
640     })
641
642     it('Should fail with an unknown element', async function () {
643       const params = getBase({ videoId: videoId2, expectedStatus: 404 })
644       await removeVideoFromPlaylist(params)
645     })
646
647     it('Succeed with the correct params', async function () {
648       const params = getBase({ expectedStatus: 204 })
649       await removeVideoFromPlaylist(params)
650     })
651   })
652
653   describe('When deleting a playlist', function () {
654     it('Should fail with an unknown playlist', async function () {
655       await deleteVideoPlaylist(server.url, server.accessToken, 42, 404)
656     })
657
658     it('Should fail with a playlist of another user', async function () {
659       await deleteVideoPlaylist(server.url, userAccessToken, playlistUUID, 403)
660     })
661
662     it('Should fail with the watch later playlist', async function () {
663       await deleteVideoPlaylist(server.url, server.accessToken, watchLaterPlaylistId, 400)
664     })
665
666     it('Should succeed with the correct params', async function () {
667       await deleteVideoPlaylist(server.url, server.accessToken, playlistUUID)
668     })
669   })
670
671   after(async function () {
672     await cleanupTests([ server ])
673   })
674 })