1 /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
6 VideoPlaylistCreateResult,
7 VideoPlaylistElementCreate,
8 VideoPlaylistElementUpdate,
12 } from '@shared/models'
13 import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
15 checkBadCountPagination,
16 checkBadSortPagination,
17 checkBadStartPagination,
20 generateUserAccessToken,
25 setAccessTokensToServers,
26 setDefaultVideoChannel,
28 } from '../../../../shared/extra-utils'
30 describe('Test video playlists API validator', function () {
31 let server: ServerInfo
32 let userAccessToken: string
34 let playlist: VideoPlaylistCreateResult
35 let privatePlaylistUUID: string
37 let watchLaterPlaylistId: number
41 let command: PlaylistsCommand
43 // ---------------------------------------------------------------
45 before(async function () {
48 server = await flushAndRunServer(1)
50 await setAccessTokensToServers([ server ])
51 await setDefaultVideoChannel([ server ])
53 userAccessToken = await generateUserAccessToken(server, 'user1')
54 videoId = (await uploadVideoAndGetId({ server, videoName: 'video 1' })).id
56 command = server.playlistsCommand
59 const { data } = await command.listByAccount({
60 token: server.accessToken,
64 playlistType: VideoPlaylistType.WATCH_LATER
66 watchLaterPlaylistId = data[0].id
70 playlist = await command.create({
72 displayName: 'super playlist',
73 privacy: VideoPlaylistPrivacy.PUBLIC,
74 videoChannelId: server.videoChannel.id
80 const created = await command.create({
82 displayName: 'private',
83 privacy: VideoPlaylistPrivacy.PRIVATE
86 privatePlaylistUUID = created.uuid
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'
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)
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)
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)
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 } })
119 it('Should fail with a bad account parameter', async function () {
120 const accountPath = '/api/v1/accounts/root2/video-playlists'
122 await makeGetRequest({
125 statusCodeExpected: HttpStatusCode.NOT_FOUND_404,
126 token: server.accessToken
130 it('Should fail with a bad video channel parameter', async function () {
131 const accountPath = '/api/v1/video-channels/bad_channel/video-playlists'
133 await makeGetRequest({
136 statusCodeExpected: HttpStatusCode.NOT_FOUND_404,
137 token: server.accessToken
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({
146 path: videoChannelPath,
147 statusCodeExpected: HttpStatusCode.OK_200,
148 token: server.accessToken
153 describe('When listing videos of a playlist', function () {
154 const path = '/api/v1/video-playlists/'
156 it('Should fail with a bad start pagination', async function () {
157 await checkBadStartPagination(server.url, path + playlist.shortUUID + '/videos', server.accessToken)
160 it('Should fail with a bad count pagination', async function () {
161 await checkBadCountPagination(server.url, path + playlist.shortUUID + '/videos', server.accessToken)
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 })
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 })
174 it('Should fail with an unknown playlist', async function () {
175 await command.get({ playlistId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
178 it('Should fail to get an unlisted playlist with the number id', async function () {
179 const playlist = await command.create({
181 displayName: 'super playlist',
182 videoChannelId: server.videoChannel.id,
183 privacy: VideoPlaylistPrivacy.UNLISTED
187 await command.get({ playlistId: playlist.id, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
188 await command.get({ playlistId: playlist.uuid, expectedStatus: HttpStatusCode.OK_200 })
191 it('Should succeed with the correct params', async function () {
192 await command.get({ playlistId: playlist.uuid, expectedStatus: HttpStatusCode.OK_200 })
196 describe('When creating/updating a video playlist', function () {
198 attributes?: Partial<VideoPlaylistCreate>,
199 wrapper?: Partial<Parameters<PlaylistsCommand['create']>[0]>
203 displayName: 'display name',
204 privacy: VideoPlaylistPrivacy.UNLISTED,
205 thumbnailfile: 'thumbnail.jpg',
206 videoChannelId: server.videoChannel.id,
211 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
216 const getUpdate = (params: any, playlistId: number | string) => {
217 return immutableAssign(params, { playlistId: playlistId })
220 it('Should fail with an unauthenticated user', async function () {
221 const params = getBase({}, { token: null, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
223 await command.create(params)
224 await command.update(getUpdate(params, playlist.shortUUID))
227 it('Should fail without displayName', async function () {
228 const params = getBase({ displayName: undefined })
230 await command.create(params)
233 it('Should fail with an incorrect display name', async function () {
234 const params = getBase({ displayName: 's'.repeat(300) })
236 await command.create(params)
237 await command.update(getUpdate(params, playlist.shortUUID))
240 it('Should fail with an incorrect description', async function () {
241 const params = getBase({ description: 't' })
243 await command.create(params)
244 await command.update(getUpdate(params, playlist.shortUUID))
247 it('Should fail with an incorrect privacy', async function () {
248 const params = getBase({ privacy: 45 })
250 await command.create(params)
251 await command.update(getUpdate(params, playlist.shortUUID))
254 it('Should fail with an unknown video channel id', async function () {
255 const params = getBase({ videoChannelId: 42 }, { expectedStatus: HttpStatusCode.NOT_FOUND_404 })
257 await command.create(params)
258 await command.update(getUpdate(params, playlist.shortUUID))
261 it('Should fail with an incorrect thumbnail file', async function () {
262 const params = getBase({ thumbnailfile: 'video_short.mp4' })
264 await command.create(params)
265 await command.update(getUpdate(params, playlist.shortUUID))
268 it('Should fail with a thumbnail file too big', async function () {
269 const params = getBase({ thumbnailfile: 'preview-big.png' })
271 await command.create(params)
272 await command.update(getUpdate(params, playlist.shortUUID))
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 })
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))
287 it('Should fail with an unknown playlist to update', async function () {
288 await command.update(getUpdate(
289 getBase({}, { expectedStatus: HttpStatusCode.NOT_FOUND_404 }),
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 }),
301 it('Should fail to update the watch later playlist', async function () {
302 await command.update(getUpdate(
303 getBase({}, { expectedStatus: HttpStatusCode.BAD_REQUEST_400 }),
308 it('Should succeed with the correct params', async function () {
310 const params = getBase({}, { expectedStatus: HttpStatusCode.OK_200 })
311 await command.create(params)
315 const params = getBase({}, { expectedStatus: HttpStatusCode.NO_CONTENT_204 })
316 await command.update(getUpdate(params, playlist.shortUUID))
321 describe('When adding an element in a playlist', function () {
323 attributes?: Partial<VideoPlaylistElementCreate>,
324 wrapper?: Partial<Parameters<PlaylistsCommand['addElement']>[0]>
335 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
336 playlistId: playlist.id,
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)
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)
352 it('Should fail with an unknown or incorrect playlist id', async function () {
354 const params = getBase({}, { playlistId: 'toto' })
355 await command.addElement(params)
359 const params = getBase({}, { playlistId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
360 await command.addElement(params)
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)
369 it('Should fail with a bad start/stop timestamp', async function () {
371 const params = getBase({ startTimestamp: -42 })
372 await command.addElement(params)
376 const params = getBase({ stopTimestamp: 'toto' as any })
377 await command.addElement(params)
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
388 describe('When updating an element in a playlist', function () {
390 attributes?: Partial<VideoPlaylistElementUpdate>,
391 wrapper?: Partial<Parameters<PlaylistsCommand['updateElement']>[0]>
402 playlistId: playlist.id,
403 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
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)
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)
419 it('Should fail with an unknown or incorrect playlist id', async function () {
421 const params = getBase({}, { playlistId: 'toto' })
422 await command.updateElement(params)
426 const params = getBase({}, { playlistId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
427 await command.updateElement(params)
431 it('Should fail with an unknown or incorrect playlistElement id', async function () {
433 const params = getBase({}, { elementId: 'toto' })
434 await command.updateElement(params)
438 const params = getBase({}, { elementId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
439 await command.updateElement(params)
443 it('Should fail with a bad start/stop timestamp', async function () {
445 const params = getBase({ startTimestamp: 'toto' as any })
446 await command.updateElement(params)
450 const params = getBase({ stopTimestamp: -42 })
451 await command.updateElement(params)
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)
460 it('Succeed with the correct params', async function () {
461 const params = getBase({}, { expectedStatus: HttpStatusCode.NO_CONTENT_204 })
462 await command.updateElement(params)
466 describe('When reordering elements of a playlist', function () {
471 attributes?: Partial<VideoPlaylistReorder>,
472 wrapper?: Partial<Parameters<PlaylistsCommand['reorderElements']>[0]>
477 insertAfterPosition: 2,
483 playlistId: playlist.shortUUID,
484 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
490 before(async function () {
491 videoId3 = (await uploadVideoAndGetId({ server, videoName: 'video 3' })).id
492 videoId4 = (await uploadVideoAndGetId({ server, videoName: 'video 4' })).id
494 for (const id of [ videoId3, videoId4 ]) {
495 await command.addElement({ playlistId: playlist.shortUUID, attributes: { videoId: id } })
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)
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)
509 it('Should fail with an invalid playlist', async function () {
511 const params = getBase({}, { playlistId: 'toto' })
512 await command.reorderElements(params)
516 const params = getBase({}, { playlistId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
517 await command.reorderElements(params)
521 it('Should fail with an invalid start position', async function () {
523 const params = getBase({ startPosition: -1 })
524 await command.reorderElements(params)
528 const params = getBase({ startPosition: 'toto' as any })
529 await command.reorderElements(params)
533 const params = getBase({ startPosition: 42 })
534 await command.reorderElements(params)
538 it('Should fail with an invalid insert after position', async function () {
540 const params = getBase({ insertAfterPosition: 'toto' as any })
541 await command.reorderElements(params)
545 const params = getBase({ insertAfterPosition: -2 })
546 await command.reorderElements(params)
550 const params = getBase({ insertAfterPosition: 42 })
551 await command.reorderElements(params)
555 it('Should fail with an invalid reorder length', async function () {
557 const params = getBase({ reorderLength: 'toto' as any })
558 await command.reorderElements(params)
562 const params = getBase({ reorderLength: -2 })
563 await command.reorderElements(params)
567 const params = getBase({ reorderLength: 42 })
568 await command.reorderElements(params)
572 it('Succeed with the correct params', async function () {
573 const params = getBase({}, { expectedStatus: HttpStatusCode.NO_CONTENT_204 })
574 await command.reorderElements(params)
578 describe('When checking exists in playlist endpoint', function () {
579 const path = '/api/v1/users/me/video-playlists/videos-exist'
581 it('Should fail with an unauthenticated user', async function () {
582 await makeGetRequest({
585 query: { videoIds: [ 1, 2 ] },
586 statusCodeExpected: HttpStatusCode.UNAUTHORIZED_401
590 it('Should fail with invalid video ids', async function () {
591 await makeGetRequest({
593 token: server.accessToken,
595 query: { videoIds: 'toto' }
598 await makeGetRequest({
600 token: server.accessToken,
602 query: { videoIds: [ 'toto' ] }
605 await makeGetRequest({
607 token: server.accessToken,
609 query: { videoIds: [ 1, 'toto' ] }
613 it('Should succeed with the correct params', async function () {
614 await makeGetRequest({
616 token: server.accessToken,
618 query: { videoIds: [ 1, 2 ] },
619 statusCodeExpected: HttpStatusCode.OK_200
624 describe('When deleting an element in a playlist', function () {
625 const getBase = (wrapper: Partial<Parameters<PlaylistsCommand['removeElement']>[0]>) => {
628 playlistId: playlist.uuid,
629 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
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)
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)
645 it('Should fail with an unknown or incorrect playlist id', async function () {
647 const params = getBase({ playlistId: 'toto' })
648 await command.removeElement(params)
652 const params = getBase({ playlistId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
653 await command.removeElement(params)
657 it('Should fail with an unknown or incorrect video id', async function () {
659 const params = getBase({ elementId: 'toto' as any })
660 await command.removeElement(params)
664 const params = getBase({ elementId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
665 await command.removeElement(params)
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)
674 it('Succeed with the correct params', async function () {
675 const params = getBase({ expectedStatus: HttpStatusCode.NO_CONTENT_204 })
676 await command.removeElement(params)
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 })
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 })
689 it('Should fail with the watch later playlist', async function () {
690 await command.delete({ playlistId: watchLaterPlaylistId, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
693 it('Should succeed with the correct params', async function () {
694 await command.delete({ playlistId: playlist.uuid })
698 after(async function () {
699 await cleanupTests([ server ])