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