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