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