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