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