1 /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
4 import * as chai from 'chai'
11 flushAndRunMultipleServers,
13 getVideosListWithToken,
15 setAccessTokensToServers,
19 } from '@shared/extra-utils'
20 import { UserNotificationType, Video } from '@shared/models'
22 const expect = chai.expect
24 async function checkAllVideos (server: ServerInfo, token: string) {
26 const res = await getVideosListWithToken(server.url, token)
28 expect(res.body.data).to.have.lengthOf(5)
32 const res = await getVideosList(server.url)
34 expect(res.body.data).to.have.lengthOf(5)
38 async function checkAllComments (server: ServerInfo, token: string, videoUUID: string) {
39 const { data } = await server.commentsCommand.listThreads({ videoId: videoUUID, start: 0, count: 25, sort: '-createdAt', token })
41 const threads = data.filter(t => t.isDeleted === false)
42 expect(threads).to.have.lengthOf(2)
44 for (const thread of threads) {
45 const tree = await server.commentsCommand.getThread({ videoId: videoUUID, threadId: thread.id, token })
46 expect(tree.children).to.have.lengthOf(1)
50 async function checkCommentNotification (
51 mainServer: ServerInfo,
52 comment: { server: ServerInfo, token: string, videoUUID: string, text: string },
53 check: 'presence' | 'absence'
55 const command = comment.server.commentsCommand
57 const { threadId, createdAt } = await command.createThread({ token: comment.token, videoId: comment.videoUUID, text: comment.text })
59 await waitJobs([ mainServer, comment.server ])
61 const { data } = await mainServer.notificationsCommand.list({ start: 0, count: 30 })
62 const commentNotifications = data.filter(n => n.comment && n.comment.video.uuid === comment.videoUUID && n.createdAt >= createdAt)
64 if (check === 'presence') expect(commentNotifications).to.have.lengthOf(1)
65 else expect(commentNotifications).to.have.lengthOf(0)
67 await command.delete({ token: comment.token, videoId: comment.videoUUID, commentId: threadId })
69 await waitJobs([ mainServer, comment.server ])
72 describe('Test blocklist', function () {
73 let servers: ServerInfo[]
74 let videoUUID1: string
75 let videoUUID2: string
76 let videoUUID3: string
77 let userToken1: string
78 let userModeratorToken: string
79 let userToken2: string
81 let command: BlocklistCommand
82 let commentsCommand: CommentsCommand[]
84 before(async function () {
87 servers = await flushAndRunMultipleServers(3)
88 await setAccessTokensToServers(servers)
90 command = servers[0].blocklistCommand
91 commentsCommand = servers.map(s => s.commentsCommand)
94 const user = { username: 'user1', password: 'password' }
95 await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, username: user.username, password: user.password })
97 userToken1 = await userLogin(servers[0], user)
98 await uploadVideo(servers[0].url, userToken1, { name: 'video user 1' })
102 const user = { username: 'moderator', password: 'password' }
103 await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, username: user.username, password: user.password })
105 userModeratorToken = await userLogin(servers[0], user)
109 const user = { username: 'user2', password: 'password' }
110 await createUser({ url: servers[1].url, accessToken: servers[1].accessToken, username: user.username, password: user.password })
112 userToken2 = await userLogin(servers[1], user)
113 await uploadVideo(servers[1].url, userToken2, { name: 'video user 2' })
117 const res = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video server 1' })
118 videoUUID1 = res.body.video.uuid
122 const res = await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'video server 2' })
123 videoUUID2 = res.body.video.uuid
127 const res = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video 2 server 1' })
128 videoUUID3 = res.body.video.uuid
131 await doubleFollow(servers[0], servers[1])
132 await doubleFollow(servers[0], servers[2])
135 const created = await commentsCommand[0].createThread({ videoId: videoUUID1, text: 'comment root 1' })
136 const reply = await commentsCommand[0].addReply({
139 toCommentId: created.id,
140 text: 'comment user 1'
142 await commentsCommand[0].addReply({ videoId: videoUUID1, toCommentId: reply.id, text: 'comment root 1' })
146 const created = await commentsCommand[0].createThread({ token: userToken1, videoId: videoUUID1, text: 'comment user 1' })
147 await commentsCommand[0].addReply({ videoId: videoUUID1, toCommentId: created.id, text: 'comment root 1' })
150 await waitJobs(servers)
153 describe('User blocklist', function () {
155 describe('When managing account blocklist', function () {
156 it('Should list all videos', function () {
157 return checkAllVideos(servers[0], servers[0].accessToken)
160 it('Should list the comments', function () {
161 return checkAllComments(servers[0], servers[0].accessToken, videoUUID1)
164 it('Should block a remote account', async function () {
165 await command.addToMyBlocklist({ account: 'user2@localhost:' + servers[1].port })
168 it('Should hide its videos', async function () {
169 const res = await getVideosListWithToken(servers[0].url, servers[0].accessToken)
171 const videos: Video[] = res.body.data
172 expect(videos).to.have.lengthOf(4)
174 const v = videos.find(v => v.name === 'video user 2')
175 expect(v).to.be.undefined
178 it('Should block a local account', async function () {
179 await command.addToMyBlocklist({ account: 'user1' })
182 it('Should hide its videos', async function () {
183 const res = await getVideosListWithToken(servers[0].url, servers[0].accessToken)
185 const videos: Video[] = res.body.data
186 expect(videos).to.have.lengthOf(3)
188 const v = videos.find(v => v.name === 'video user 1')
189 expect(v).to.be.undefined
192 it('Should hide its comments', async function () {
193 const { data } = await commentsCommand[0].listThreads({
194 token: servers[0].accessToken,
201 expect(data).to.have.lengthOf(1)
202 expect(data[0].totalReplies).to.equal(1)
204 const t = data.find(t => t.text === 'comment user 1')
205 expect(t).to.be.undefined
207 for (const thread of data) {
208 const tree = await commentsCommand[0].getThread({
211 token: servers[0].accessToken
213 expect(tree.children).to.have.lengthOf(0)
217 it('Should not have notifications from blocked accounts', async function () {
221 const comment = { server: servers[0], token: userToken1, videoUUID: videoUUID1, text: 'hidden comment' }
222 await checkCommentNotification(servers[0], comment, 'absence')
229 videoUUID: videoUUID2,
230 text: 'hello @root@localhost:' + servers[0].port
232 await checkCommentNotification(servers[0], comment, 'absence')
236 it('Should list all the videos with another user', async function () {
237 return checkAllVideos(servers[0], userToken1)
240 it('Should list blocked accounts', async function () {
242 const body = await command.listMyAccountBlocklist({ start: 0, count: 1, sort: 'createdAt' })
243 expect(body.total).to.equal(2)
245 const block = body.data[0]
246 expect(block.byAccount.displayName).to.equal('root')
247 expect(block.byAccount.name).to.equal('root')
248 expect(block.blockedAccount.displayName).to.equal('user2')
249 expect(block.blockedAccount.name).to.equal('user2')
250 expect(block.blockedAccount.host).to.equal('localhost:' + servers[1].port)
254 const body = await command.listMyAccountBlocklist({ start: 1, count: 2, sort: 'createdAt' })
255 expect(body.total).to.equal(2)
257 const block = body.data[0]
258 expect(block.byAccount.displayName).to.equal('root')
259 expect(block.byAccount.name).to.equal('root')
260 expect(block.blockedAccount.displayName).to.equal('user1')
261 expect(block.blockedAccount.name).to.equal('user1')
262 expect(block.blockedAccount.host).to.equal('localhost:' + servers[0].port)
266 it('Should not allow a remote blocked user to comment my videos', async function () {
270 await commentsCommand[1].createThread({ token: userToken2, videoId: videoUUID3, text: 'comment user 2' })
271 await waitJobs(servers)
273 await commentsCommand[0].createThread({ token: servers[0].accessToken, videoId: videoUUID3, text: 'uploader' })
274 await waitJobs(servers)
276 const commentId = await commentsCommand[1].findCommentId({ videoId: videoUUID3, text: 'uploader' })
277 const message = 'reply by user 2'
278 const reply = await commentsCommand[1].addReply({ token: userToken2, videoId: videoUUID3, toCommentId: commentId, text: message })
279 await commentsCommand[1].addReply({ videoId: videoUUID3, toCommentId: reply.id, text: 'another reply' })
281 await waitJobs(servers)
284 // Server 2 has all the comments
286 const { data } = await commentsCommand[1].listThreads({ videoId: videoUUID3, count: 25, sort: '-createdAt' })
288 expect(data).to.have.lengthOf(2)
289 expect(data[0].text).to.equal('uploader')
290 expect(data[1].text).to.equal('comment user 2')
292 const tree = await commentsCommand[1].getThread({ videoId: videoUUID3, threadId: data[0].id })
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 { data } = await server.commentsCommand.listThreads({ videoId: videoUUID3, count: 25, sort: '-createdAt' })
303 expect(data).to.have.lengthOf(1)
304 expect(data[0].text).to.equal('uploader')
306 const tree = await server.commentsCommand.getThread({ videoId: videoUUID3, threadId: data[0].id })
308 if (server.serverNumber === 1) expect(tree.children).to.have.lengthOf(0)
309 else expect(tree.children).to.have.lengthOf(1)
313 it('Should unblock the remote account', async function () {
314 await command.removeFromMyBlocklist({ account: 'user2@localhost:' + servers[1].port })
317 it('Should display its videos', async function () {
318 const res = await getVideosListWithToken(servers[0].url, servers[0].accessToken)
320 const videos: Video[] = res.body.data
321 expect(videos).to.have.lengthOf(4)
323 const v = videos.find(v => v.name === 'video user 2')
324 expect(v).not.to.be.undefined
327 it('Should display its comments on my video', async function () {
328 for (const server of servers) {
329 const { data } = await server.commentsCommand.listThreads({ videoId: videoUUID3, count: 25, sort: '-createdAt' })
331 // Server 3 should not have 2 comment threads, because server 1 did not forward the server 2 comment
332 if (server.serverNumber === 3) {
333 expect(data).to.have.lengthOf(1)
337 expect(data).to.have.lengthOf(2)
338 expect(data[0].text).to.equal('uploader')
339 expect(data[1].text).to.equal('comment user 2')
341 const tree = await server.commentsCommand.getThread({ videoId: videoUUID3, threadId: data[0].id })
342 expect(tree.children).to.have.lengthOf(1)
343 expect(tree.children[0].comment.text).to.equal('reply by user 2')
344 expect(tree.children[0].children).to.have.lengthOf(1)
345 expect(tree.children[0].children[0].comment.text).to.equal('another reply')
349 it('Should unblock the local account', async function () {
350 await command.removeFromMyBlocklist({ account: 'user1' })
353 it('Should display its comments', function () {
354 return checkAllComments(servers[0], servers[0].accessToken, videoUUID1)
357 it('Should have a notification from a non blocked account', async function () {
361 const comment = { server: servers[1], token: userToken2, videoUUID: videoUUID1, text: 'displayed comment' }
362 await checkCommentNotification(servers[0], comment, 'presence')
369 videoUUID: videoUUID2,
370 text: 'hello @root@localhost:' + servers[0].port
372 await checkCommentNotification(servers[0], comment, 'presence')
377 describe('When managing server blocklist', function () {
379 it('Should list all videos', function () {
380 return checkAllVideos(servers[0], servers[0].accessToken)
383 it('Should list the comments', function () {
384 return checkAllComments(servers[0], servers[0].accessToken, videoUUID1)
387 it('Should block a remote server', async function () {
388 await command.addToMyBlocklist({ server: 'localhost:' + servers[1].port })
391 it('Should hide its videos', async function () {
392 const res = await getVideosListWithToken(servers[0].url, servers[0].accessToken)
394 const videos: Video[] = res.body.data
395 expect(videos).to.have.lengthOf(3)
397 const v1 = videos.find(v => v.name === 'video user 2')
398 const v2 = videos.find(v => v.name === 'video server 2')
400 expect(v1).to.be.undefined
401 expect(v2).to.be.undefined
404 it('Should list all the videos with another user', async function () {
405 return checkAllVideos(servers[0], userToken1)
408 it('Should hide its comments', async function () {
411 const { id } = await commentsCommand[1].createThread({ token: userToken2, videoId: videoUUID1, text: 'hidden comment 2' })
413 await waitJobs(servers)
415 await checkAllComments(servers[0], servers[0].accessToken, videoUUID1)
417 await commentsCommand[1].delete({ token: userToken2, videoId: videoUUID1, commentId: id })
420 it('Should not have notifications from blocked server', async function () {
424 const comment = { server: servers[1], token: userToken2, videoUUID: videoUUID1, text: 'hidden comment' }
425 await checkCommentNotification(servers[0], comment, 'absence')
432 videoUUID: videoUUID1,
433 text: 'hello @root@localhost:' + servers[0].port
435 await checkCommentNotification(servers[0], comment, 'absence')
439 it('Should list blocked servers', async function () {
440 const body = await command.listMyServerBlocklist({ start: 0, count: 1, sort: 'createdAt' })
441 expect(body.total).to.equal(1)
443 const block = body.data[0]
444 expect(block.byAccount.displayName).to.equal('root')
445 expect(block.byAccount.name).to.equal('root')
446 expect(block.blockedServer.host).to.equal('localhost:' + servers[1].port)
449 it('Should unblock the remote server', async function () {
450 await command.removeFromMyBlocklist({ server: 'localhost:' + servers[1].port })
453 it('Should display its videos', function () {
454 return checkAllVideos(servers[0], servers[0].accessToken)
457 it('Should display its comments', function () {
458 return checkAllComments(servers[0], servers[0].accessToken, videoUUID1)
461 it('Should have notification from unblocked server', async function () {
465 const comment = { server: servers[1], token: userToken2, videoUUID: videoUUID1, text: 'displayed comment' }
466 await checkCommentNotification(servers[0], comment, 'presence')
473 videoUUID: videoUUID1,
474 text: 'hello @root@localhost:' + servers[0].port
476 await checkCommentNotification(servers[0], comment, 'presence')
482 describe('Server blocklist', function () {
484 describe('When managing account blocklist', function () {
485 it('Should list all videos', async function () {
486 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
487 await checkAllVideos(servers[0], token)
491 it('Should list the comments', async function () {
492 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
493 await checkAllComments(servers[0], token, videoUUID1)
497 it('Should block a remote account', async function () {
498 await command.addToServerBlocklist({ account: 'user2@localhost:' + servers[1].port })
501 it('Should hide its videos', async function () {
502 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
503 const res = await getVideosListWithToken(servers[0].url, token)
505 const videos: Video[] = res.body.data
506 expect(videos).to.have.lengthOf(4)
508 const v = videos.find(v => v.name === 'video user 2')
509 expect(v).to.be.undefined
513 it('Should block a local account', async function () {
514 await command.addToServerBlocklist({ account: 'user1' })
517 it('Should hide its videos', async function () {
518 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
519 const res = await getVideosListWithToken(servers[0].url, token)
521 const videos: Video[] = res.body.data
522 expect(videos).to.have.lengthOf(3)
524 const v = videos.find(v => v.name === 'video user 1')
525 expect(v).to.be.undefined
529 it('Should hide its comments', async function () {
530 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
531 const { data } = await commentsCommand[0].listThreads({ videoId: videoUUID1, count: 20, sort: '-createdAt', token })
532 const threads = data.filter(t => t.isDeleted === false)
534 expect(threads).to.have.lengthOf(1)
535 expect(threads[0].totalReplies).to.equal(1)
537 const t = threads.find(t => t.text === 'comment user 1')
538 expect(t).to.be.undefined
540 for (const thread of threads) {
541 const tree = await commentsCommand[0].getThread({ videoId: videoUUID1, threadId: thread.id, token })
542 expect(tree.children).to.have.lengthOf(0)
547 it('Should not have notification from blocked accounts by instance', async function () {
551 const comment = { server: servers[0], token: userToken1, videoUUID: videoUUID1, text: 'hidden comment' }
552 await checkCommentNotification(servers[0], comment, 'absence')
559 videoUUID: videoUUID1,
560 text: 'hello @root@localhost:' + servers[0].port
562 await checkCommentNotification(servers[0], comment, 'absence')
566 it('Should list blocked accounts', async function () {
568 const body = await command.listServerAccountBlocklist({ start: 0, count: 1, sort: 'createdAt' })
569 expect(body.total).to.equal(2)
571 const block = body.data[0]
572 expect(block.byAccount.displayName).to.equal('peertube')
573 expect(block.byAccount.name).to.equal('peertube')
574 expect(block.blockedAccount.displayName).to.equal('user2')
575 expect(block.blockedAccount.name).to.equal('user2')
576 expect(block.blockedAccount.host).to.equal('localhost:' + servers[1].port)
580 const body = await command.listServerAccountBlocklist({ start: 1, count: 2, sort: 'createdAt' })
581 expect(body.total).to.equal(2)
583 const block = body.data[0]
584 expect(block.byAccount.displayName).to.equal('peertube')
585 expect(block.byAccount.name).to.equal('peertube')
586 expect(block.blockedAccount.displayName).to.equal('user1')
587 expect(block.blockedAccount.name).to.equal('user1')
588 expect(block.blockedAccount.host).to.equal('localhost:' + servers[0].port)
592 it('Should unblock the remote account', async function () {
593 await command.removeFromServerBlocklist({ account: 'user2@localhost:' + servers[1].port })
596 it('Should display its videos', async function () {
597 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
598 const res = await getVideosListWithToken(servers[0].url, token)
600 const videos: Video[] = res.body.data
601 expect(videos).to.have.lengthOf(4)
603 const v = videos.find(v => v.name === 'video user 2')
604 expect(v).not.to.be.undefined
608 it('Should unblock the local account', async function () {
609 await command.removeFromServerBlocklist({ account: 'user1' })
612 it('Should display its comments', async function () {
613 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
614 await checkAllComments(servers[0], token, videoUUID1)
618 it('Should have notifications from unblocked accounts', async function () {
622 const comment = { server: servers[0], token: userToken1, videoUUID: videoUUID1, text: 'displayed comment' }
623 await checkCommentNotification(servers[0], comment, 'presence')
630 videoUUID: videoUUID1,
631 text: 'hello @root@localhost:' + servers[0].port
633 await checkCommentNotification(servers[0], comment, 'presence')
638 describe('When managing server blocklist', function () {
639 it('Should list all videos', async function () {
640 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
641 await checkAllVideos(servers[0], token)
645 it('Should list the comments', async function () {
646 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
647 await checkAllComments(servers[0], token, videoUUID1)
651 it('Should block a remote server', async function () {
652 await command.addToServerBlocklist({ server: 'localhost:' + servers[1].port })
655 it('Should hide its videos', async function () {
656 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
657 const res1 = await getVideosList(servers[0].url)
658 const res2 = await getVideosListWithToken(servers[0].url, token)
660 for (const res of [ res1, res2 ]) {
661 const videos: Video[] = res.body.data
662 expect(videos).to.have.lengthOf(3)
664 const v1 = videos.find(v => v.name === 'video user 2')
665 const v2 = videos.find(v => v.name === 'video server 2')
667 expect(v1).to.be.undefined
668 expect(v2).to.be.undefined
673 it('Should hide its comments', async function () {
676 const { id } = await commentsCommand[1].createThread({ token: userToken2, videoId: videoUUID1, text: 'hidden comment 2' })
678 await waitJobs(servers)
680 await checkAllComments(servers[0], servers[0].accessToken, videoUUID1)
682 await commentsCommand[1].delete({ token: userToken2, videoId: videoUUID1, commentId: id })
685 it('Should not have notification from blocked instances by instance', async function () {
689 const comment = { server: servers[1], token: userToken2, videoUUID: videoUUID1, text: 'hidden comment' }
690 await checkCommentNotification(servers[0], comment, 'absence')
697 videoUUID: videoUUID1,
698 text: 'hello @root@localhost:' + servers[0].port
700 await checkCommentNotification(servers[0], comment, 'absence')
704 const now = new Date()
705 await servers[1].followsCommand.unfollow({ target: servers[0] })
706 await waitJobs(servers)
707 await servers[1].followsCommand.follow({ targets: [ servers[0].host ] })
709 await waitJobs(servers)
711 const { data } = await servers[0].notificationsCommand.list({ start: 0, count: 30 })
712 const commentNotifications = data.filter(n => {
713 return n.type === UserNotificationType.NEW_INSTANCE_FOLLOWER && n.createdAt >= now.toISOString()
716 expect(commentNotifications).to.have.lengthOf(0)
720 it('Should list blocked servers', async function () {
721 const body = await command.listServerServerBlocklist({ start: 0, count: 1, sort: 'createdAt' })
722 expect(body.total).to.equal(1)
724 const block = body.data[0]
725 expect(block.byAccount.displayName).to.equal('peertube')
726 expect(block.byAccount.name).to.equal('peertube')
727 expect(block.blockedServer.host).to.equal('localhost:' + servers[1].port)
730 it('Should unblock the remote server', async function () {
731 await command.removeFromServerBlocklist({ server: 'localhost:' + servers[1].port })
734 it('Should list all videos', async function () {
735 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
736 await checkAllVideos(servers[0], token)
740 it('Should list the comments', async function () {
741 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
742 await checkAllComments(servers[0], token, videoUUID1)
746 it('Should have notification from unblocked instances', async function () {
750 const comment = { server: servers[1], token: userToken2, videoUUID: videoUUID1, text: 'displayed comment' }
751 await checkCommentNotification(servers[0], comment, 'presence')
758 videoUUID: videoUUID1,
759 text: 'hello @root@localhost:' + servers[0].port
761 await checkCommentNotification(servers[0], comment, 'presence')
765 const now = new Date()
766 await servers[1].followsCommand.unfollow({ target: servers[0] })
767 await waitJobs(servers)
768 await servers[1].followsCommand.follow({ targets: [ servers[0].host ] })
770 await waitJobs(servers)
772 const { data } = await servers[0].notificationsCommand.list({ start: 0, count: 30 })
773 const commentNotifications = data.filter(n => {
774 return n.type === UserNotificationType.NEW_INSTANCE_FOLLOWER && n.createdAt >= now.toISOString()
777 expect(commentNotifications).to.have.lengthOf(1)
783 after(async function () {
784 await cleanupTests(servers)