1 /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
5 checkBadCountPagination,
6 checkBadSortPagination,
7 checkBadStartPagination,
13 setAccessTokensToServers,
14 setDefaultVideoChannel
15 } from '@shared/extra-utils'
19 VideoPlaylistCreateResult,
20 VideoPlaylistElementCreate,
21 VideoPlaylistElementUpdate,
25 } from '@shared/models'
27 describe('Test video playlists API validator', function () {
28 let server: PeerTubeServer
29 let userAccessToken: string
31 let playlist: VideoPlaylistCreateResult
32 let privatePlaylistUUID: string
34 let watchLaterPlaylistId: number
38 let command: PlaylistsCommand
40 // ---------------------------------------------------------------
42 before(async function () {
45 server = await createSingleServer(1)
47 await setAccessTokensToServers([ server ])
48 await setDefaultVideoChannel([ server ])
50 userAccessToken = await server.users.generateUserAndToken('user1')
51 videoId = (await server.videos.quickUpload({ name: 'video 1' })).id
53 command = server.playlists
56 const { data } = await command.listByAccount({
57 token: server.accessToken,
61 playlistType: VideoPlaylistType.WATCH_LATER
63 watchLaterPlaylistId = data[0].id
67 playlist = await command.create({
69 displayName: 'super playlist',
70 privacy: VideoPlaylistPrivacy.PUBLIC,
71 videoChannelId: server.store.channel.id
77 const created = await command.create({
79 displayName: 'private',
80 privacy: VideoPlaylistPrivacy.PRIVATE
83 privatePlaylistUUID = created.uuid
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'
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)
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)
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)
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 } })
116 it('Should fail with a bad account parameter', async function () {
117 const accountPath = '/api/v1/accounts/root2/video-playlists'
119 await makeGetRequest({
122 expectedStatus: HttpStatusCode.NOT_FOUND_404,
123 token: server.accessToken
127 it('Should fail with a bad video channel parameter', async function () {
128 const accountPath = '/api/v1/video-channels/bad_channel/video-playlists'
130 await makeGetRequest({
133 expectedStatus: HttpStatusCode.NOT_FOUND_404,
134 token: server.accessToken
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({
143 path: videoChannelPath,
144 expectedStatus: HttpStatusCode.OK_200,
145 token: server.accessToken
150 describe('When listing videos of a playlist', function () {
151 const path = '/api/v1/video-playlists/'
153 it('Should fail with a bad start pagination', async function () {
154 await checkBadStartPagination(server.url, path + playlist.shortUUID + '/videos', server.accessToken)
157 it('Should fail with a bad count pagination', async function () {
158 await checkBadCountPagination(server.url, path + playlist.shortUUID + '/videos', server.accessToken)
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 })
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 })
171 it('Should fail with an unknown playlist', async function () {
172 await command.get({ playlistId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
175 it('Should fail to get an unlisted playlist with the number id', async function () {
176 const playlist = await command.create({
178 displayName: 'super playlist',
179 videoChannelId: server.store.channel.id,
180 privacy: VideoPlaylistPrivacy.UNLISTED
184 await command.get({ playlistId: playlist.id, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
185 await command.get({ playlistId: playlist.uuid, expectedStatus: HttpStatusCode.OK_200 })
188 it('Should succeed with the correct params', async function () {
189 await command.get({ playlistId: playlist.uuid, expectedStatus: HttpStatusCode.OK_200 })
193 describe('When creating/updating a video playlist', function () {
195 attributes?: Partial<VideoPlaylistCreate>,
196 wrapper?: Partial<Parameters<PlaylistsCommand['create']>[0]>
200 displayName: 'display name',
201 privacy: VideoPlaylistPrivacy.UNLISTED,
202 thumbnailfile: 'thumbnail.jpg',
203 videoChannelId: server.store.channel.id,
208 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
213 const getUpdate = (params: any, playlistId: number | string) => {
214 return { ...params, playlistId: playlistId }
217 it('Should fail with an unauthenticated user', async function () {
218 const params = getBase({}, { token: null, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
220 await command.create(params)
221 await command.update(getUpdate(params, playlist.shortUUID))
224 it('Should fail without displayName', async function () {
225 const params = getBase({ displayName: undefined })
227 await command.create(params)
230 it('Should fail with an incorrect display name', async function () {
231 const params = getBase({ displayName: 's'.repeat(300) })
233 await command.create(params)
234 await command.update(getUpdate(params, playlist.shortUUID))
237 it('Should fail with an incorrect description', async function () {
238 const params = getBase({ description: 't' })
240 await command.create(params)
241 await command.update(getUpdate(params, playlist.shortUUID))
244 it('Should fail with an incorrect privacy', async function () {
245 const params = getBase({ privacy: 45 })
247 await command.create(params)
248 await command.update(getUpdate(params, playlist.shortUUID))
251 it('Should fail with an unknown video channel id', async function () {
252 const params = getBase({ videoChannelId: 42 }, { expectedStatus: HttpStatusCode.NOT_FOUND_404 })
254 await command.create(params)
255 await command.update(getUpdate(params, playlist.shortUUID))
258 it('Should fail with an incorrect thumbnail file', async function () {
259 const params = getBase({ thumbnailfile: 'video_short.mp4' })
261 await command.create(params)
262 await command.update(getUpdate(params, playlist.shortUUID))
265 it('Should fail with a thumbnail file too big', async function () {
266 const params = getBase({ thumbnailfile: 'preview-big.png' })
268 await command.create(params)
269 await command.update(getUpdate(params, playlist.shortUUID))
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 })
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))
284 it('Should fail with an unknown playlist to update', async function () {
285 await command.update(getUpdate(
286 getBase({}, { expectedStatus: HttpStatusCode.NOT_FOUND_404 }),
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 }),
298 it('Should fail to update the watch later playlist', async function () {
299 await command.update(getUpdate(
300 getBase({}, { expectedStatus: HttpStatusCode.BAD_REQUEST_400 }),
305 it('Should succeed with the correct params', async function () {
307 const params = getBase({}, { expectedStatus: HttpStatusCode.OK_200 })
308 await command.create(params)
312 const params = getBase({}, { expectedStatus: HttpStatusCode.NO_CONTENT_204 })
313 await command.update(getUpdate(params, playlist.shortUUID))
318 describe('When adding an element in a playlist', function () {
320 attributes?: Partial<VideoPlaylistElementCreate>,
321 wrapper?: Partial<Parameters<PlaylistsCommand['addElement']>[0]>
332 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
333 playlistId: playlist.id,
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)
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)
349 it('Should fail with an unknown or incorrect playlist id', async function () {
351 const params = getBase({}, { playlistId: 'toto' })
352 await command.addElement(params)
356 const params = getBase({}, { playlistId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
357 await command.addElement(params)
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)
366 it('Should fail with a bad start/stop timestamp', async function () {
368 const params = getBase({ startTimestamp: -42 })
369 await command.addElement(params)
373 const params = getBase({ stopTimestamp: 'toto' as any })
374 await command.addElement(params)
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
385 describe('When updating an element in a playlist', function () {
387 attributes?: Partial<VideoPlaylistElementUpdate>,
388 wrapper?: Partial<Parameters<PlaylistsCommand['updateElement']>[0]>
399 playlistId: playlist.id,
400 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
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)
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)
416 it('Should fail with an unknown or incorrect playlist id', async function () {
418 const params = getBase({}, { playlistId: 'toto' })
419 await command.updateElement(params)
423 const params = getBase({}, { playlistId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
424 await command.updateElement(params)
428 it('Should fail with an unknown or incorrect playlistElement id', async function () {
430 const params = getBase({}, { elementId: 'toto' })
431 await command.updateElement(params)
435 const params = getBase({}, { elementId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
436 await command.updateElement(params)
440 it('Should fail with a bad start/stop timestamp', async function () {
442 const params = getBase({ startTimestamp: 'toto' as any })
443 await command.updateElement(params)
447 const params = getBase({ stopTimestamp: -42 })
448 await command.updateElement(params)
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)
457 it('Succeed with the correct params', async function () {
458 const params = getBase({}, { expectedStatus: HttpStatusCode.NO_CONTENT_204 })
459 await command.updateElement(params)
463 describe('When reordering elements of a playlist', function () {
468 attributes?: Partial<VideoPlaylistReorder>,
469 wrapper?: Partial<Parameters<PlaylistsCommand['reorderElements']>[0]>
474 insertAfterPosition: 2,
480 playlistId: playlist.shortUUID,
481 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
487 before(async function () {
488 videoId3 = (await server.videos.quickUpload({ name: 'video 3' })).id
489 videoId4 = (await server.videos.quickUpload({ name: 'video 4' })).id
491 for (const id of [ videoId3, videoId4 ]) {
492 await command.addElement({ playlistId: playlist.shortUUID, attributes: { videoId: id } })
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)
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)
506 it('Should fail with an invalid playlist', async function () {
508 const params = getBase({}, { playlistId: 'toto' })
509 await command.reorderElements(params)
513 const params = getBase({}, { playlistId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
514 await command.reorderElements(params)
518 it('Should fail with an invalid start position', async function () {
520 const params = getBase({ startPosition: -1 })
521 await command.reorderElements(params)
525 const params = getBase({ startPosition: 'toto' as any })
526 await command.reorderElements(params)
530 const params = getBase({ startPosition: 42 })
531 await command.reorderElements(params)
535 it('Should fail with an invalid insert after position', async function () {
537 const params = getBase({ insertAfterPosition: 'toto' as any })
538 await command.reorderElements(params)
542 const params = getBase({ insertAfterPosition: -2 })
543 await command.reorderElements(params)
547 const params = getBase({ insertAfterPosition: 42 })
548 await command.reorderElements(params)
552 it('Should fail with an invalid reorder length', async function () {
554 const params = getBase({ reorderLength: 'toto' as any })
555 await command.reorderElements(params)
559 const params = getBase({ reorderLength: -2 })
560 await command.reorderElements(params)
564 const params = getBase({ reorderLength: 42 })
565 await command.reorderElements(params)
569 it('Succeed with the correct params', async function () {
570 const params = getBase({}, { expectedStatus: HttpStatusCode.NO_CONTENT_204 })
571 await command.reorderElements(params)
575 describe('When checking exists in playlist endpoint', function () {
576 const path = '/api/v1/users/me/video-playlists/videos-exist'
578 it('Should fail with an unauthenticated user', async function () {
579 await makeGetRequest({
582 query: { videoIds: [ 1, 2 ] },
583 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
587 it('Should fail with invalid video ids', async function () {
588 await makeGetRequest({
590 token: server.accessToken,
592 query: { videoIds: 'toto' }
595 await makeGetRequest({
597 token: server.accessToken,
599 query: { videoIds: [ 'toto' ] }
602 await makeGetRequest({
604 token: server.accessToken,
606 query: { videoIds: [ 1, 'toto' ] }
610 it('Should succeed with the correct params', async function () {
611 await makeGetRequest({
613 token: server.accessToken,
615 query: { videoIds: [ 1, 2 ] },
616 expectedStatus: HttpStatusCode.OK_200
621 describe('When deleting an element in a playlist', function () {
622 const getBase = (wrapper: Partial<Parameters<PlaylistsCommand['removeElement']>[0]>) => {
625 playlistId: playlist.uuid,
626 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
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)
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)
642 it('Should fail with an unknown or incorrect playlist id', async function () {
644 const params = getBase({ playlistId: 'toto' })
645 await command.removeElement(params)
649 const params = getBase({ playlistId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
650 await command.removeElement(params)
654 it('Should fail with an unknown or incorrect video id', async function () {
656 const params = getBase({ elementId: 'toto' as any })
657 await command.removeElement(params)
661 const params = getBase({ elementId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
662 await command.removeElement(params)
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)
671 it('Succeed with the correct params', async function () {
672 const params = getBase({ expectedStatus: HttpStatusCode.NO_CONTENT_204 })
673 await command.removeElement(params)
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 })
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 })
686 it('Should fail with the watch later playlist', async function () {
687 await command.delete({ playlistId: watchLaterPlaylistId, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
690 it('Should succeed with the correct params', async function () {
691 await command.delete({ playlistId: playlist.uuid })
695 after(async function () {
696 await cleanupTests([ server ])