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