1 /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
4 import { HttpStatusCode } from '@shared/core-utils'
7 VideoPlaylistCreateResult,
8 VideoPlaylistElementCreate,
9 VideoPlaylistElementUpdate,
13 } from '@shared/models'
15 checkBadCountPagination,
16 checkBadSortPagination,
17 checkBadStartPagination,
20 generateUserAccessToken,
24 setAccessTokensToServers,
25 setDefaultVideoChannel,
27 } from '../../../../shared/extra-utils'
29 describe('Test video playlists API validator', function () {
30 let server: ServerInfo
31 let userAccessToken: string
33 let playlist: VideoPlaylistCreateResult
34 let privatePlaylistUUID: string
36 let watchLaterPlaylistId: number
40 let command: PlaylistsCommand
42 // ---------------------------------------------------------------
44 before(async function () {
47 server = await flushAndRunServer(1)
49 await setAccessTokensToServers([ server ])
50 await setDefaultVideoChannel([ server ])
52 userAccessToken = await generateUserAccessToken(server, 'user1')
53 videoId = (await uploadVideoAndGetId({ server, videoName: 'video 1' })).id
55 command = server.playlistsCommand
58 const { data } = await command.listByAccount({
59 token: server.accessToken,
63 playlistType: VideoPlaylistType.WATCH_LATER
65 watchLaterPlaylistId = data[0].id
69 playlist = await command.create({
71 displayName: 'super playlist',
72 privacy: VideoPlaylistPrivacy.PUBLIC,
73 videoChannelId: server.videoChannel.id
79 const created = await command.create({
81 displayName: 'private',
82 privacy: VideoPlaylistPrivacy.PRIVATE
85 privatePlaylistUUID = created.uuid
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'
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)
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)
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)
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 } })
118 it('Should fail with a bad account parameter', async function () {
119 const accountPath = '/api/v1/accounts/root2/video-playlists'
121 await makeGetRequest({
124 statusCodeExpected: HttpStatusCode.NOT_FOUND_404,
125 token: server.accessToken
129 it('Should fail with a bad video channel parameter', async function () {
130 const accountPath = '/api/v1/video-channels/bad_channel/video-playlists'
132 await makeGetRequest({
135 statusCodeExpected: HttpStatusCode.NOT_FOUND_404,
136 token: server.accessToken
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({
145 path: videoChannelPath,
146 statusCodeExpected: HttpStatusCode.OK_200,
147 token: server.accessToken
152 describe('When listing videos of a playlist', function () {
153 const path = '/api/v1/video-playlists/'
155 it('Should fail with a bad start pagination', async function () {
156 await checkBadStartPagination(server.url, path + playlist.shortUUID + '/videos', server.accessToken)
159 it('Should fail with a bad count pagination', async function () {
160 await checkBadCountPagination(server.url, path + playlist.shortUUID + '/videos', server.accessToken)
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 })
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 })
173 it('Should fail with an unknown playlist', async function () {
174 await command.get({ playlistId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
177 it('Should fail to get an unlisted playlist with the number id', async function () {
178 const playlist = await command.create({
180 displayName: 'super playlist',
181 videoChannelId: server.videoChannel.id,
182 privacy: VideoPlaylistPrivacy.UNLISTED
186 await command.get({ playlistId: playlist.id, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
187 await command.get({ playlistId: playlist.uuid, expectedStatus: HttpStatusCode.OK_200 })
190 it('Should succeed with the correct params', async function () {
191 await command.get({ playlistId: playlist.uuid, expectedStatus: HttpStatusCode.OK_200 })
195 describe('When creating/updating a video playlist', function () {
197 attributes?: Partial<VideoPlaylistCreate>,
198 wrapper?: Partial<Parameters<PlaylistsCommand['create']>[0]>
202 displayName: 'display name',
203 privacy: VideoPlaylistPrivacy.UNLISTED,
204 thumbnailfile: 'thumbnail.jpg',
205 videoChannelId: server.videoChannel.id,
210 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
215 const getUpdate = (params: any, playlistId: number | string) => {
216 return { ...params, playlistId: playlistId }
219 it('Should fail with an unauthenticated user', async function () {
220 const params = getBase({}, { token: null, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
222 await command.create(params)
223 await command.update(getUpdate(params, playlist.shortUUID))
226 it('Should fail without displayName', async function () {
227 const params = getBase({ displayName: undefined })
229 await command.create(params)
232 it('Should fail with an incorrect display name', async function () {
233 const params = getBase({ displayName: 's'.repeat(300) })
235 await command.create(params)
236 await command.update(getUpdate(params, playlist.shortUUID))
239 it('Should fail with an incorrect description', async function () {
240 const params = getBase({ description: 't' })
242 await command.create(params)
243 await command.update(getUpdate(params, playlist.shortUUID))
246 it('Should fail with an incorrect privacy', async function () {
247 const params = getBase({ privacy: 45 })
249 await command.create(params)
250 await command.update(getUpdate(params, playlist.shortUUID))
253 it('Should fail with an unknown video channel id', async function () {
254 const params = getBase({ videoChannelId: 42 }, { expectedStatus: HttpStatusCode.NOT_FOUND_404 })
256 await command.create(params)
257 await command.update(getUpdate(params, playlist.shortUUID))
260 it('Should fail with an incorrect thumbnail file', async function () {
261 const params = getBase({ thumbnailfile: 'video_short.mp4' })
263 await command.create(params)
264 await command.update(getUpdate(params, playlist.shortUUID))
267 it('Should fail with a thumbnail file too big', async function () {
268 const params = getBase({ thumbnailfile: 'preview-big.png' })
270 await command.create(params)
271 await command.update(getUpdate(params, playlist.shortUUID))
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 })
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))
286 it('Should fail with an unknown playlist to update', async function () {
287 await command.update(getUpdate(
288 getBase({}, { expectedStatus: HttpStatusCode.NOT_FOUND_404 }),
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 }),
300 it('Should fail to update the watch later playlist', async function () {
301 await command.update(getUpdate(
302 getBase({}, { expectedStatus: HttpStatusCode.BAD_REQUEST_400 }),
307 it('Should succeed with the correct params', async function () {
309 const params = getBase({}, { expectedStatus: HttpStatusCode.OK_200 })
310 await command.create(params)
314 const params = getBase({}, { expectedStatus: HttpStatusCode.NO_CONTENT_204 })
315 await command.update(getUpdate(params, playlist.shortUUID))
320 describe('When adding an element in a playlist', function () {
322 attributes?: Partial<VideoPlaylistElementCreate>,
323 wrapper?: Partial<Parameters<PlaylistsCommand['addElement']>[0]>
334 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
335 playlistId: playlist.id,
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)
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)
351 it('Should fail with an unknown or incorrect playlist id', async function () {
353 const params = getBase({}, { playlistId: 'toto' })
354 await command.addElement(params)
358 const params = getBase({}, { playlistId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
359 await command.addElement(params)
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)
368 it('Should fail with a bad start/stop timestamp', async function () {
370 const params = getBase({ startTimestamp: -42 })
371 await command.addElement(params)
375 const params = getBase({ stopTimestamp: 'toto' as any })
376 await command.addElement(params)
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
387 describe('When updating an element in a playlist', function () {
389 attributes?: Partial<VideoPlaylistElementUpdate>,
390 wrapper?: Partial<Parameters<PlaylistsCommand['updateElement']>[0]>
401 playlistId: playlist.id,
402 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
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)
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)
418 it('Should fail with an unknown or incorrect playlist id', async function () {
420 const params = getBase({}, { playlistId: 'toto' })
421 await command.updateElement(params)
425 const params = getBase({}, { playlistId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
426 await command.updateElement(params)
430 it('Should fail with an unknown or incorrect playlistElement id', async function () {
432 const params = getBase({}, { elementId: 'toto' })
433 await command.updateElement(params)
437 const params = getBase({}, { elementId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
438 await command.updateElement(params)
442 it('Should fail with a bad start/stop timestamp', async function () {
444 const params = getBase({ startTimestamp: 'toto' as any })
445 await command.updateElement(params)
449 const params = getBase({ stopTimestamp: -42 })
450 await command.updateElement(params)
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)
459 it('Succeed with the correct params', async function () {
460 const params = getBase({}, { expectedStatus: HttpStatusCode.NO_CONTENT_204 })
461 await command.updateElement(params)
465 describe('When reordering elements of a playlist', function () {
470 attributes?: Partial<VideoPlaylistReorder>,
471 wrapper?: Partial<Parameters<PlaylistsCommand['reorderElements']>[0]>
476 insertAfterPosition: 2,
482 playlistId: playlist.shortUUID,
483 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
489 before(async function () {
490 videoId3 = (await uploadVideoAndGetId({ server, videoName: 'video 3' })).id
491 videoId4 = (await uploadVideoAndGetId({ server, videoName: 'video 4' })).id
493 for (const id of [ videoId3, videoId4 ]) {
494 await command.addElement({ playlistId: playlist.shortUUID, attributes: { videoId: id } })
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)
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)
508 it('Should fail with an invalid playlist', async function () {
510 const params = getBase({}, { playlistId: 'toto' })
511 await command.reorderElements(params)
515 const params = getBase({}, { playlistId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
516 await command.reorderElements(params)
520 it('Should fail with an invalid start position', async function () {
522 const params = getBase({ startPosition: -1 })
523 await command.reorderElements(params)
527 const params = getBase({ startPosition: 'toto' as any })
528 await command.reorderElements(params)
532 const params = getBase({ startPosition: 42 })
533 await command.reorderElements(params)
537 it('Should fail with an invalid insert after position', async function () {
539 const params = getBase({ insertAfterPosition: 'toto' as any })
540 await command.reorderElements(params)
544 const params = getBase({ insertAfterPosition: -2 })
545 await command.reorderElements(params)
549 const params = getBase({ insertAfterPosition: 42 })
550 await command.reorderElements(params)
554 it('Should fail with an invalid reorder length', async function () {
556 const params = getBase({ reorderLength: 'toto' as any })
557 await command.reorderElements(params)
561 const params = getBase({ reorderLength: -2 })
562 await command.reorderElements(params)
566 const params = getBase({ reorderLength: 42 })
567 await command.reorderElements(params)
571 it('Succeed with the correct params', async function () {
572 const params = getBase({}, { expectedStatus: HttpStatusCode.NO_CONTENT_204 })
573 await command.reorderElements(params)
577 describe('When checking exists in playlist endpoint', function () {
578 const path = '/api/v1/users/me/video-playlists/videos-exist'
580 it('Should fail with an unauthenticated user', async function () {
581 await makeGetRequest({
584 query: { videoIds: [ 1, 2 ] },
585 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401
589 it('Should fail with invalid video ids', async function () {
590 await makeGetRequest({
592 token: server.accessToken,
594 query: { videoIds: 'toto' }
597 await makeGetRequest({
599 token: server.accessToken,
601 query: { videoIds: [ 'toto' ] }
604 await makeGetRequest({
606 token: server.accessToken,
608 query: { videoIds: [ 1, 'toto' ] }
612 it('Should succeed with the correct params', async function () {
613 await makeGetRequest({
615 token: server.accessToken,
617 query: { videoIds: [ 1, 2 ] },
618 statusCodeExpected: HttpStatusCode.OK_200
623 describe('When deleting an element in a playlist', function () {
624 const getBase = (wrapper: Partial<Parameters<PlaylistsCommand['removeElement']>[0]>) => {
627 playlistId: playlist.uuid,
628 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
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)
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)
644 it('Should fail with an unknown or incorrect playlist id', async function () {
646 const params = getBase({ playlistId: 'toto' })
647 await command.removeElement(params)
651 const params = getBase({ playlistId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
652 await command.removeElement(params)
656 it('Should fail with an unknown or incorrect video id', async function () {
658 const params = getBase({ elementId: 'toto' as any })
659 await command.removeElement(params)
663 const params = getBase({ elementId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
664 await command.removeElement(params)
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)
673 it('Succeed with the correct params', async function () {
674 const params = getBase({ expectedStatus: HttpStatusCode.NO_CONTENT_204 })
675 await command.removeElement(params)
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 })
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 })
688 it('Should fail with the watch later playlist', async function () {
689 await command.delete({ playlistId: watchLaterPlaylistId, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
692 it('Should succeed with the correct params', async function () {
693 await command.delete({ playlistId: playlist.uuid })
697 after(async function () {
698 await cleanupTests([ server ])