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