1 /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
4 import * as chai from 'chai'
14 flushAndRunMultipleServers,
16 getVideoCommentThreads,
18 getVideosListWithToken,
19 getVideoThreadComments,
21 setAccessTokensToServers,
25 } from '@shared/extra-utils'
26 import { UserNotification, UserNotificationType, Video, VideoComment, VideoCommentThreadTree } from '@shared/models'
28 const expect = chai.expect
30 async function checkAllVideos (url: string, token: string) {
32 const res = await getVideosListWithToken(url, token)
34 expect(res.body.data).to.have.lengthOf(5)
38 const res = await getVideosList(url)
40 expect(res.body.data).to.have.lengthOf(5)
44 async function checkAllComments (url: string, token: string, videoUUID: string) {
45 const resThreads = await getVideoCommentThreads(url, videoUUID, 0, 25, '-createdAt', token)
47 const allThreads: VideoComment[] = resThreads.body.data
48 const threads = allThreads.filter(t => t.isDeleted === false)
49 expect(threads).to.have.lengthOf(2)
51 for (const thread of threads) {
52 const res = await getVideoThreadComments(url, videoUUID, thread.id, token)
54 const tree: VideoCommentThreadTree = res.body
55 expect(tree.children).to.have.lengthOf(1)
59 async function checkCommentNotification (
60 mainServer: ServerInfo,
61 comment: { server: ServerInfo, token: string, videoUUID: string, text: string },
62 check: 'presence' | 'absence'
64 const resComment = await addVideoCommentThread(comment.server.url, comment.token, comment.videoUUID, comment.text)
65 const created = resComment.body.comment as VideoComment
66 const threadId = created.id
67 const createdAt = created.createdAt
69 await waitJobs([ mainServer, comment.server ])
71 const res = await getUserNotifications(mainServer.url, mainServer.accessToken, 0, 30)
72 const commentNotifications = (res.body.data as UserNotification[])
73 .filter(n => n.comment && n.comment.video.uuid === comment.videoUUID && n.createdAt >= createdAt)
75 if (check === 'presence') expect(commentNotifications).to.have.lengthOf(1)
76 else expect(commentNotifications).to.have.lengthOf(0)
78 await deleteVideoComment(comment.server.url, comment.token, comment.videoUUID, threadId)
80 await waitJobs([ mainServer, comment.server ])
83 describe('Test blocklist', function () {
84 let servers: ServerInfo[]
85 let videoUUID1: string
86 let videoUUID2: string
87 let videoUUID3: string
88 let userToken1: string
89 let userModeratorToken: string
90 let userToken2: string
92 let command: BlocklistCommand
94 before(async function () {
97 servers = await flushAndRunMultipleServers(3)
98 await setAccessTokensToServers(servers)
101 const user = { username: 'user1', password: 'password' }
102 await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, username: user.username, password: user.password })
104 userToken1 = await userLogin(servers[0], user)
105 await uploadVideo(servers[0].url, userToken1, { name: 'video user 1' })
109 const user = { username: 'moderator', password: 'password' }
110 await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, username: user.username, password: user.password })
112 userModeratorToken = await userLogin(servers[0], user)
116 const user = { username: 'user2', password: 'password' }
117 await createUser({ url: servers[1].url, accessToken: servers[1].accessToken, username: user.username, password: user.password })
119 userToken2 = await userLogin(servers[1], user)
120 await uploadVideo(servers[1].url, userToken2, { name: 'video user 2' })
124 const res = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video server 1' })
125 videoUUID1 = res.body.video.uuid
129 const res = await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'video server 2' })
130 videoUUID2 = res.body.video.uuid
134 const res = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video 2 server 1' })
135 videoUUID3 = res.body.video.uuid
138 await doubleFollow(servers[0], servers[1])
139 await doubleFollow(servers[0], servers[2])
142 const resComment = await addVideoCommentThread(servers[0].url, servers[0].accessToken, videoUUID1, 'comment root 1')
143 const resReply = await addVideoCommentReply(servers[0].url, userToken1, videoUUID1, resComment.body.comment.id, 'comment user 1')
144 await addVideoCommentReply(servers[0].url, servers[0].accessToken, videoUUID1, resReply.body.comment.id, 'comment root 1')
148 const resComment = await addVideoCommentThread(servers[0].url, userToken1, videoUUID1, 'comment user 1')
149 await addVideoCommentReply(servers[0].url, servers[0].accessToken, videoUUID1, resComment.body.comment.id, 'comment root 1')
152 await waitJobs(servers)
154 command = servers[0].blocklistCommand
157 describe('User blocklist', function () {
159 describe('When managing account blocklist', function () {
160 it('Should list all videos', function () {
161 return checkAllVideos(servers[0].url, servers[0].accessToken)
164 it('Should list the comments', function () {
165 return checkAllComments(servers[0].url, servers[0].accessToken, videoUUID1)
168 it('Should block a remote account', async function () {
169 await command.addToMyBlocklist({ account: 'user2@localhost:' + servers[1].port })
172 it('Should hide its videos', async function () {
173 const res = await getVideosListWithToken(servers[0].url, servers[0].accessToken)
175 const videos: Video[] = res.body.data
176 expect(videos).to.have.lengthOf(4)
178 const v = videos.find(v => v.name === 'video user 2')
179 expect(v).to.be.undefined
182 it('Should block a local account', async function () {
183 await command.addToMyBlocklist({ account: 'user1' })
186 it('Should hide its videos', async function () {
187 const res = await getVideosListWithToken(servers[0].url, servers[0].accessToken)
189 const videos: Video[] = res.body.data
190 expect(videos).to.have.lengthOf(3)
192 const v = videos.find(v => v.name === 'video user 1')
193 expect(v).to.be.undefined
196 it('Should hide its comments', async function () {
197 const resThreads = await getVideoCommentThreads(servers[0].url, videoUUID1, 0, 25, '-createdAt', servers[0].accessToken)
199 const threads: VideoComment[] = resThreads.body.data
200 expect(threads).to.have.lengthOf(1)
201 expect(threads[0].totalReplies).to.equal(1)
203 const t = threads.find(t => t.text === 'comment user 1')
204 expect(t).to.be.undefined
206 for (const thread of threads) {
207 const res = await getVideoThreadComments(servers[0].url, videoUUID1, thread.id, servers[0].accessToken)
209 const tree: VideoCommentThreadTree = res.body
210 expect(tree.children).to.have.lengthOf(0)
214 it('Should not have notifications from blocked accounts', async function () {
218 const comment = { server: servers[0], token: userToken1, videoUUID: videoUUID1, text: 'hidden comment' }
219 await checkCommentNotification(servers[0], comment, 'absence')
226 videoUUID: videoUUID2,
227 text: 'hello @root@localhost:' + servers[0].port
229 await checkCommentNotification(servers[0], comment, 'absence')
233 it('Should list all the videos with another user', async function () {
234 return checkAllVideos(servers[0].url, userToken1)
237 it('Should list blocked accounts', async function () {
239 const body = await command.listMyAccountBlocklist({ start: 0, count: 1, sort: 'createdAt' })
240 expect(body.total).to.equal(2)
242 const block = body.data[0]
243 expect(block.byAccount.displayName).to.equal('root')
244 expect(block.byAccount.name).to.equal('root')
245 expect(block.blockedAccount.displayName).to.equal('user2')
246 expect(block.blockedAccount.name).to.equal('user2')
247 expect(block.blockedAccount.host).to.equal('localhost:' + servers[1].port)
251 const body = await command.listMyAccountBlocklist({ start: 1, count: 2, sort: 'createdAt' })
252 expect(body.total).to.equal(2)
254 const block = body.data[0]
255 expect(block.byAccount.displayName).to.equal('root')
256 expect(block.byAccount.name).to.equal('root')
257 expect(block.blockedAccount.displayName).to.equal('user1')
258 expect(block.blockedAccount.name).to.equal('user1')
259 expect(block.blockedAccount.host).to.equal('localhost:' + servers[0].port)
263 it('Should not allow a remote blocked user to comment my videos', async function () {
267 await addVideoCommentThread(servers[1].url, userToken2, videoUUID3, 'comment user 2')
268 await waitJobs(servers)
270 await addVideoCommentThread(servers[0].url, servers[0].accessToken, videoUUID3, 'uploader')
271 await waitJobs(servers)
273 const commentId = await findCommentId(servers[1].url, videoUUID3, 'uploader')
274 const message = 'reply by user 2'
275 const resReply = await addVideoCommentReply(servers[1].url, userToken2, videoUUID3, commentId, message)
276 await addVideoCommentReply(servers[1].url, servers[1].accessToken, videoUUID3, resReply.body.comment.id, 'another reply')
278 await waitJobs(servers)
281 // Server 2 has all the comments
283 const resThreads = await getVideoCommentThreads(servers[1].url, videoUUID3, 0, 25, '-createdAt')
284 const threads: VideoComment[] = resThreads.body.data
286 expect(threads).to.have.lengthOf(2)
287 expect(threads[0].text).to.equal('uploader')
288 expect(threads[1].text).to.equal('comment user 2')
290 const resReplies = await getVideoThreadComments(servers[1].url, videoUUID3, threads[0].id)
292 const tree: VideoCommentThreadTree = resReplies.body
293 expect(tree.children).to.have.lengthOf(1)
294 expect(tree.children[0].comment.text).to.equal('reply by user 2')
295 expect(tree.children[0].children).to.have.lengthOf(1)
296 expect(tree.children[0].children[0].comment.text).to.equal('another reply')
299 // Server 1 and 3 should only have uploader comments
300 for (const server of [ servers[0], servers[2] ]) {
301 const resThreads = await getVideoCommentThreads(server.url, videoUUID3, 0, 25, '-createdAt')
302 const threads: VideoComment[] = resThreads.body.data
304 expect(threads).to.have.lengthOf(1)
305 expect(threads[0].text).to.equal('uploader')
307 const resReplies = await getVideoThreadComments(server.url, videoUUID3, threads[0].id)
309 const tree: VideoCommentThreadTree = resReplies.body
310 if (server.serverNumber === 1) {
311 expect(tree.children).to.have.lengthOf(0)
313 expect(tree.children).to.have.lengthOf(1)
318 it('Should unblock the remote account', async function () {
319 await command.removeFromMyBlocklist({ account: 'user2@localhost:' + servers[1].port })
322 it('Should display its videos', async function () {
323 const res = await getVideosListWithToken(servers[0].url, servers[0].accessToken)
325 const videos: Video[] = res.body.data
326 expect(videos).to.have.lengthOf(4)
328 const v = videos.find(v => v.name === 'video user 2')
329 expect(v).not.to.be.undefined
332 it('Should display its comments on my video', async function () {
333 for (const server of servers) {
334 const resThreads = await getVideoCommentThreads(server.url, videoUUID3, 0, 25, '-createdAt')
335 const threads: VideoComment[] = resThreads.body.data
337 // Server 3 should not have 2 comment threads, because server 1 did not forward the server 2 comment
338 if (server.serverNumber === 3) {
339 expect(threads).to.have.lengthOf(1)
343 expect(threads).to.have.lengthOf(2)
344 expect(threads[0].text).to.equal('uploader')
345 expect(threads[1].text).to.equal('comment user 2')
347 const resReplies = await getVideoThreadComments(server.url, videoUUID3, threads[0].id)
349 const tree: VideoCommentThreadTree = resReplies.body
350 expect(tree.children).to.have.lengthOf(1)
351 expect(tree.children[0].comment.text).to.equal('reply by user 2')
352 expect(tree.children[0].children).to.have.lengthOf(1)
353 expect(tree.children[0].children[0].comment.text).to.equal('another reply')
357 it('Should unblock the local account', async function () {
358 await command.removeFromMyBlocklist({ account: 'user1' })
361 it('Should display its comments', function () {
362 return checkAllComments(servers[0].url, servers[0].accessToken, videoUUID1)
365 it('Should have a notification from a non blocked account', async function () {
369 const comment = { server: servers[1], token: userToken2, videoUUID: videoUUID1, text: 'displayed comment' }
370 await checkCommentNotification(servers[0], comment, 'presence')
377 videoUUID: videoUUID2,
378 text: 'hello @root@localhost:' + servers[0].port
380 await checkCommentNotification(servers[0], comment, 'presence')
385 describe('When managing server blocklist', function () {
387 it('Should list all videos', function () {
388 return checkAllVideos(servers[0].url, servers[0].accessToken)
391 it('Should list the comments', function () {
392 return checkAllComments(servers[0].url, servers[0].accessToken, videoUUID1)
395 it('Should block a remote server', async function () {
396 await command.addToMyBlocklist({ server: 'localhost:' + servers[1].port })
399 it('Should hide its videos', async function () {
400 const res = await getVideosListWithToken(servers[0].url, servers[0].accessToken)
402 const videos: Video[] = res.body.data
403 expect(videos).to.have.lengthOf(3)
405 const v1 = videos.find(v => v.name === 'video user 2')
406 const v2 = videos.find(v => v.name === 'video server 2')
408 expect(v1).to.be.undefined
409 expect(v2).to.be.undefined
412 it('Should list all the videos with another user', async function () {
413 return checkAllVideos(servers[0].url, userToken1)
416 it('Should hide its comments', async function () {
419 const resThreads = await addVideoCommentThread(servers[1].url, userToken2, videoUUID1, 'hidden comment 2')
420 const threadId = resThreads.body.comment.id
422 await waitJobs(servers)
424 await checkAllComments(servers[0].url, servers[0].accessToken, videoUUID1)
426 await deleteVideoComment(servers[1].url, userToken2, videoUUID1, threadId)
429 it('Should not have notifications from blocked server', async function () {
433 const comment = { server: servers[1], token: userToken2, videoUUID: videoUUID1, text: 'hidden comment' }
434 await checkCommentNotification(servers[0], comment, 'absence')
441 videoUUID: videoUUID1,
442 text: 'hello @root@localhost:' + servers[0].port
444 await checkCommentNotification(servers[0], comment, 'absence')
448 it('Should list blocked servers', async function () {
449 const body = await command.listMyServerBlocklist({ start: 0, count: 1, sort: 'createdAt' })
450 expect(body.total).to.equal(1)
452 const block = body.data[0]
453 expect(block.byAccount.displayName).to.equal('root')
454 expect(block.byAccount.name).to.equal('root')
455 expect(block.blockedServer.host).to.equal('localhost:' + servers[1].port)
458 it('Should unblock the remote server', async function () {
459 await command.removeFromMyBlocklist({ server: 'localhost:' + servers[1].port })
462 it('Should display its videos', function () {
463 return checkAllVideos(servers[0].url, servers[0].accessToken)
466 it('Should display its comments', function () {
467 return checkAllComments(servers[0].url, servers[0].accessToken, videoUUID1)
470 it('Should have notification from unblocked server', async function () {
474 const comment = { server: servers[1], token: userToken2, videoUUID: videoUUID1, text: 'displayed comment' }
475 await checkCommentNotification(servers[0], comment, 'presence')
482 videoUUID: videoUUID1,
483 text: 'hello @root@localhost:' + servers[0].port
485 await checkCommentNotification(servers[0], comment, 'presence')
491 describe('Server blocklist', function () {
493 describe('When managing account blocklist', function () {
494 it('Should list all videos', async function () {
495 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
496 await checkAllVideos(servers[0].url, token)
500 it('Should list the comments', async function () {
501 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
502 await checkAllComments(servers[0].url, token, videoUUID1)
506 it('Should block a remote account', async function () {
507 await command.addToServerBlocklist({ account: 'user2@localhost:' + servers[1].port })
510 it('Should hide its videos', async function () {
511 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
512 const res = await getVideosListWithToken(servers[0].url, token)
514 const videos: Video[] = res.body.data
515 expect(videos).to.have.lengthOf(4)
517 const v = videos.find(v => v.name === 'video user 2')
518 expect(v).to.be.undefined
522 it('Should block a local account', async function () {
523 await command.addToServerBlocklist({ account: 'user1' })
526 it('Should hide its videos', async function () {
527 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
528 const res = await getVideosListWithToken(servers[0].url, token)
530 const videos: Video[] = res.body.data
531 expect(videos).to.have.lengthOf(3)
533 const v = videos.find(v => v.name === 'video user 1')
534 expect(v).to.be.undefined
538 it('Should hide its comments', async function () {
539 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
540 const resThreads = await getVideoCommentThreads(servers[0].url, videoUUID1, 0, 20, '-createdAt', token)
542 let threads: VideoComment[] = resThreads.body.data
543 threads = threads.filter(t => t.isDeleted === false)
545 expect(threads).to.have.lengthOf(1)
546 expect(threads[0].totalReplies).to.equal(1)
548 const t = threads.find(t => t.text === 'comment user 1')
549 expect(t).to.be.undefined
551 for (const thread of threads) {
552 const res = await getVideoThreadComments(servers[0].url, videoUUID1, thread.id, token)
554 const tree: VideoCommentThreadTree = res.body
555 expect(tree.children).to.have.lengthOf(0)
560 it('Should not have notification from blocked accounts by instance', async function () {
564 const comment = { server: servers[0], token: userToken1, videoUUID: videoUUID1, text: 'hidden comment' }
565 await checkCommentNotification(servers[0], comment, 'absence')
572 videoUUID: videoUUID1,
573 text: 'hello @root@localhost:' + servers[0].port
575 await checkCommentNotification(servers[0], comment, 'absence')
579 it('Should list blocked accounts', async function () {
581 const body = await command.listServerAccountBlocklist({ start: 0, count: 1, sort: 'createdAt' })
582 expect(body.total).to.equal(2)
584 const block = body.data[0]
585 expect(block.byAccount.displayName).to.equal('peertube')
586 expect(block.byAccount.name).to.equal('peertube')
587 expect(block.blockedAccount.displayName).to.equal('user2')
588 expect(block.blockedAccount.name).to.equal('user2')
589 expect(block.blockedAccount.host).to.equal('localhost:' + servers[1].port)
593 const body = await command.listServerAccountBlocklist({ start: 1, count: 2, sort: 'createdAt' })
594 expect(body.total).to.equal(2)
596 const block = body.data[0]
597 expect(block.byAccount.displayName).to.equal('peertube')
598 expect(block.byAccount.name).to.equal('peertube')
599 expect(block.blockedAccount.displayName).to.equal('user1')
600 expect(block.blockedAccount.name).to.equal('user1')
601 expect(block.blockedAccount.host).to.equal('localhost:' + servers[0].port)
605 it('Should unblock the remote account', async function () {
606 await command.removeFromServerBlocklist({ account: 'user2@localhost:' + servers[1].port })
609 it('Should display its videos', async function () {
610 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
611 const res = await getVideosListWithToken(servers[0].url, token)
613 const videos: Video[] = res.body.data
614 expect(videos).to.have.lengthOf(4)
616 const v = videos.find(v => v.name === 'video user 2')
617 expect(v).not.to.be.undefined
621 it('Should unblock the local account', async function () {
622 await command.removeFromServerBlocklist({ account: 'user1' })
625 it('Should display its comments', async function () {
626 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
627 await checkAllComments(servers[0].url, token, videoUUID1)
631 it('Should have notifications from unblocked accounts', async function () {
635 const comment = { server: servers[0], token: userToken1, videoUUID: videoUUID1, text: 'displayed comment' }
636 await checkCommentNotification(servers[0], comment, 'presence')
643 videoUUID: videoUUID1,
644 text: 'hello @root@localhost:' + servers[0].port
646 await checkCommentNotification(servers[0], comment, 'presence')
651 describe('When managing server blocklist', function () {
652 it('Should list all videos', async function () {
653 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
654 await checkAllVideos(servers[0].url, token)
658 it('Should list the comments', async function () {
659 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
660 await checkAllComments(servers[0].url, token, videoUUID1)
664 it('Should block a remote server', async function () {
665 await command.addToServerBlocklist({ server: 'localhost:' + servers[1].port })
668 it('Should hide its videos', async function () {
669 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
670 const res1 = await getVideosList(servers[0].url)
671 const res2 = await getVideosListWithToken(servers[0].url, token)
673 for (const res of [ res1, res2 ]) {
674 const videos: Video[] = res.body.data
675 expect(videos).to.have.lengthOf(3)
677 const v1 = videos.find(v => v.name === 'video user 2')
678 const v2 = videos.find(v => v.name === 'video server 2')
680 expect(v1).to.be.undefined
681 expect(v2).to.be.undefined
686 it('Should hide its comments', async function () {
689 const resThreads = await addVideoCommentThread(servers[1].url, userToken2, videoUUID1, 'hidden comment 2')
690 const threadId = resThreads.body.comment.id
692 await waitJobs(servers)
694 await checkAllComments(servers[0].url, servers[0].accessToken, videoUUID1)
696 await deleteVideoComment(servers[1].url, userToken2, videoUUID1, threadId)
699 it('Should not have notification from blocked instances by instance', async function () {
703 const comment = { server: servers[1], token: userToken2, videoUUID: videoUUID1, text: 'hidden comment' }
704 await checkCommentNotification(servers[0], comment, 'absence')
711 videoUUID: videoUUID1,
712 text: 'hello @root@localhost:' + servers[0].port
714 await checkCommentNotification(servers[0], comment, 'absence')
718 const now = new Date()
719 await servers[1].followsCommand.unfollow({ target: servers[0] })
720 await waitJobs(servers)
721 await servers[1].followsCommand.follow({ targets: [ servers[0].host ] })
723 await waitJobs(servers)
725 const res = await getUserNotifications(servers[0].url, servers[0].accessToken, 0, 30)
726 const commentNotifications = (res.body.data as UserNotification[])
728 return n.type === UserNotificationType.NEW_INSTANCE_FOLLOWER &&
729 n.createdAt >= now.toISOString()
732 expect(commentNotifications).to.have.lengthOf(0)
736 it('Should list blocked servers', async function () {
737 const body = await command.listServerServerBlocklist({ start: 0, count: 1, sort: 'createdAt' })
738 expect(body.total).to.equal(1)
740 const block = body.data[0]
741 expect(block.byAccount.displayName).to.equal('peertube')
742 expect(block.byAccount.name).to.equal('peertube')
743 expect(block.blockedServer.host).to.equal('localhost:' + servers[1].port)
746 it('Should unblock the remote server', async function () {
747 await command.removeFromServerBlocklist({ server: 'localhost:' + servers[1].port })
750 it('Should list all videos', async function () {
751 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
752 await checkAllVideos(servers[0].url, token)
756 it('Should list the comments', async function () {
757 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
758 await checkAllComments(servers[0].url, token, videoUUID1)
762 it('Should have notification from unblocked instances', async function () {
766 const comment = { server: servers[1], token: userToken2, videoUUID: videoUUID1, text: 'displayed comment' }
767 await checkCommentNotification(servers[0], comment, 'presence')
774 videoUUID: videoUUID1,
775 text: 'hello @root@localhost:' + servers[0].port
777 await checkCommentNotification(servers[0], comment, 'presence')
781 const now = new Date()
782 await servers[1].followsCommand.unfollow({ target: servers[0] })
783 await waitJobs(servers)
784 await servers[1].followsCommand.follow({ targets: [ servers[0].host ] })
786 await waitJobs(servers)
788 const res = await getUserNotifications(servers[0].url, servers[0].accessToken, 0, 30)
789 const commentNotifications = (res.body.data as UserNotification[])
791 return n.type === UserNotificationType.NEW_INSTANCE_FOLLOWER &&
792 n.createdAt >= now.toISOString()
795 expect(commentNotifications).to.have.lengthOf(1)
801 after(async function () {
802 await cleanupTests(servers)