diff options
author | Chocobozzz <me@florianbigard.com> | 2020-05-22 17:06:26 +0200 |
---|---|---|
committer | Chocobozzz <chocobozzz@cpy.re> | 2020-05-29 09:32:20 +0200 |
commit | 696d83fd1377486dd03cc1bd02a21d9b6ddd9fcd (patch) | |
tree | e1b88451c4357add80721f530993e2b48d197feb /server/tests/api/users | |
parent | 72c33e716fecd1826dcf645957f8669821f91ff3 (diff) | |
download | PeerTube-696d83fd1377486dd03cc1bd02a21d9b6ddd9fcd.tar.gz PeerTube-696d83fd1377486dd03cc1bd02a21d9b6ddd9fcd.tar.zst PeerTube-696d83fd1377486dd03cc1bd02a21d9b6ddd9fcd.zip |
Block comments from muted accounts/servers
Add better control for users of comments displayed on their videos:
* Do not forward comments from muted remote accounts/servers (muted by the current server or by the video owner)
* Do not list threads and hide replies (with their children) of accounts/servers muted by the video owner
* Hide from RSS comments of muted accounts/servers by video owners
Use case:
* Try to limit spam propagation in the federation
* Add ability for users to automatically hide comments on their videos from undesirable accounts/servers (the comment section belongs to videomakers, so they choose what's posted there)
Diffstat (limited to 'server/tests/api/users')
-rw-r--r-- | server/tests/api/users/blocklist.ts | 170 |
1 files changed, 147 insertions, 23 deletions
diff --git a/server/tests/api/users/blocklist.ts b/server/tests/api/users/blocklist.ts index e37dbb5a4..8c9107a50 100644 --- a/server/tests/api/users/blocklist.ts +++ b/server/tests/api/users/blocklist.ts | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | import * as chai from 'chai' | 3 | import * as chai from 'chai' |
4 | import 'mocha' | 4 | import 'mocha' |
5 | import { AccountBlock, ServerBlock, Video } from '../../../../shared/index' | 5 | import { AccountBlock, ServerBlock, Video, UserNotification, UserNotificationType } from '../../../../shared/index' |
6 | import { | 6 | import { |
7 | cleanupTests, | 7 | cleanupTests, |
8 | createUser, | 8 | createUser, |
@@ -11,7 +11,9 @@ import { | |||
11 | flushAndRunMultipleServers, | 11 | flushAndRunMultipleServers, |
12 | ServerInfo, | 12 | ServerInfo, |
13 | uploadVideo, | 13 | uploadVideo, |
14 | userLogin | 14 | userLogin, |
15 | follow, | ||
16 | unfollow | ||
15 | } from '../../../../shared/extra-utils/index' | 17 | } from '../../../../shared/extra-utils/index' |
16 | import { setAccessTokensToServers } from '../../../../shared/extra-utils/users/login' | 18 | import { setAccessTokensToServers } from '../../../../shared/extra-utils/users/login' |
17 | import { getVideosList, getVideosListWithToken } from '../../../../shared/extra-utils/videos/videos' | 19 | import { getVideosList, getVideosListWithToken } from '../../../../shared/extra-utils/videos/videos' |
@@ -19,7 +21,8 @@ import { | |||
19 | addVideoCommentReply, | 21 | addVideoCommentReply, |
20 | addVideoCommentThread, | 22 | addVideoCommentThread, |
21 | getVideoCommentThreads, | 23 | getVideoCommentThreads, |
22 | getVideoThreadComments | 24 | getVideoThreadComments, |
25 | findCommentId | ||
23 | } from '../../../../shared/extra-utils/videos/video-comments' | 26 | } from '../../../../shared/extra-utils/videos/video-comments' |
24 | import { waitJobs } from '../../../../shared/extra-utils/server/jobs' | 27 | import { waitJobs } from '../../../../shared/extra-utils/server/jobs' |
25 | import { VideoComment, VideoCommentThreadTree } from '../../../../shared/models/videos/video-comment.model' | 28 | import { VideoComment, VideoCommentThreadTree } from '../../../../shared/models/videos/video-comment.model' |
@@ -45,13 +48,13 @@ async function checkAllVideos (url: string, token: string) { | |||
45 | { | 48 | { |
46 | const res = await getVideosListWithToken(url, token) | 49 | const res = await getVideosListWithToken(url, token) |
47 | 50 | ||
48 | expect(res.body.data).to.have.lengthOf(4) | 51 | expect(res.body.data).to.have.lengthOf(5) |
49 | } | 52 | } |
50 | 53 | ||
51 | { | 54 | { |
52 | const res = await getVideosList(url) | 55 | const res = await getVideosList(url) |
53 | 56 | ||
54 | expect(res.body.data).to.have.lengthOf(4) | 57 | expect(res.body.data).to.have.lengthOf(5) |
55 | } | 58 | } |
56 | } | 59 | } |
57 | 60 | ||
@@ -76,13 +79,15 @@ async function checkCommentNotification ( | |||
76 | check: 'presence' | 'absence' | 79 | check: 'presence' | 'absence' |
77 | ) { | 80 | ) { |
78 | const resComment = await addVideoCommentThread(comment.server.url, comment.token, comment.videoUUID, comment.text) | 81 | const resComment = await addVideoCommentThread(comment.server.url, comment.token, comment.videoUUID, comment.text) |
79 | const threadId = resComment.body.comment.id | 82 | const created = resComment.body.comment as VideoComment |
83 | const threadId = created.id | ||
84 | const createdAt = created.createdAt | ||
80 | 85 | ||
81 | await waitJobs([ mainServer, comment.server ]) | 86 | await waitJobs([ mainServer, comment.server ]) |
82 | 87 | ||
83 | const res = await getUserNotifications(mainServer.url, mainServer.accessToken, 0, 30) | 88 | const res = await getUserNotifications(mainServer.url, mainServer.accessToken, 0, 30) |
84 | const commentNotifications = res.body.data | 89 | const commentNotifications = (res.body.data as UserNotification[]) |
85 | .filter(n => n.comment && n.comment.id === threadId) | 90 | .filter(n => n.comment && n.comment.video.uuid === comment.videoUUID && n.createdAt >= createdAt) |
86 | 91 | ||
87 | if (check === 'presence') expect(commentNotifications).to.have.lengthOf(1) | 92 | if (check === 'presence') expect(commentNotifications).to.have.lengthOf(1) |
88 | else expect(commentNotifications).to.have.lengthOf(0) | 93 | else expect(commentNotifications).to.have.lengthOf(0) |
@@ -96,6 +101,7 @@ describe('Test blocklist', function () { | |||
96 | let servers: ServerInfo[] | 101 | let servers: ServerInfo[] |
97 | let videoUUID1: string | 102 | let videoUUID1: string |
98 | let videoUUID2: string | 103 | let videoUUID2: string |
104 | let videoUUID3: string | ||
99 | let userToken1: string | 105 | let userToken1: string |
100 | let userModeratorToken: string | 106 | let userModeratorToken: string |
101 | let userToken2: string | 107 | let userToken2: string |
@@ -103,7 +109,7 @@ describe('Test blocklist', function () { | |||
103 | before(async function () { | 109 | before(async function () { |
104 | this.timeout(60000) | 110 | this.timeout(60000) |
105 | 111 | ||
106 | servers = await flushAndRunMultipleServers(2) | 112 | servers = await flushAndRunMultipleServers(3) |
107 | await setAccessTokensToServers(servers) | 113 | await setAccessTokensToServers(servers) |
108 | 114 | ||
109 | { | 115 | { |
@@ -139,7 +145,13 @@ describe('Test blocklist', function () { | |||
139 | videoUUID2 = res.body.video.uuid | 145 | videoUUID2 = res.body.video.uuid |
140 | } | 146 | } |
141 | 147 | ||
148 | { | ||
149 | const res = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video 2 server 1' }) | ||
150 | videoUUID3 = res.body.video.uuid | ||
151 | } | ||
152 | |||
142 | await doubleFollow(servers[0], servers[1]) | 153 | await doubleFollow(servers[0], servers[1]) |
154 | await doubleFollow(servers[0], servers[2]) | ||
143 | 155 | ||
144 | { | 156 | { |
145 | const resComment = await addVideoCommentThread(servers[0].url, servers[0].accessToken, videoUUID1, 'comment root 1') | 157 | const resComment = await addVideoCommentThread(servers[0].url, servers[0].accessToken, videoUUID1, 'comment root 1') |
@@ -174,7 +186,7 @@ describe('Test blocklist', function () { | |||
174 | const res = await getVideosListWithToken(servers[0].url, servers[0].accessToken) | 186 | const res = await getVideosListWithToken(servers[0].url, servers[0].accessToken) |
175 | 187 | ||
176 | const videos: Video[] = res.body.data | 188 | const videos: Video[] = res.body.data |
177 | expect(videos).to.have.lengthOf(3) | 189 | expect(videos).to.have.lengthOf(4) |
178 | 190 | ||
179 | const v = videos.find(v => v.name === 'video user 2') | 191 | const v = videos.find(v => v.name === 'video user 2') |
180 | expect(v).to.be.undefined | 192 | expect(v).to.be.undefined |
@@ -188,7 +200,7 @@ describe('Test blocklist', function () { | |||
188 | const res = await getVideosListWithToken(servers[0].url, servers[0].accessToken) | 200 | const res = await getVideosListWithToken(servers[0].url, servers[0].accessToken) |
189 | 201 | ||
190 | const videos: Video[] = res.body.data | 202 | const videos: Video[] = res.body.data |
191 | expect(videos).to.have.lengthOf(2) | 203 | expect(videos).to.have.lengthOf(3) |
192 | 204 | ||
193 | const v = videos.find(v => v.name === 'video user 1') | 205 | const v = videos.find(v => v.name === 'video user 1') |
194 | expect(v).to.be.undefined | 206 | expect(v).to.be.undefined |
@@ -235,10 +247,6 @@ describe('Test blocklist', function () { | |||
235 | return checkAllVideos(servers[0].url, userToken1) | 247 | return checkAllVideos(servers[0].url, userToken1) |
236 | }) | 248 | }) |
237 | 249 | ||
238 | it('Should list all the comments with another user', async function () { | ||
239 | return checkAllComments(servers[0].url, userToken1, videoUUID1) | ||
240 | }) | ||
241 | |||
242 | it('Should list blocked accounts', async function () { | 250 | it('Should list blocked accounts', async function () { |
243 | { | 251 | { |
244 | const res = await getAccountBlocklistByAccount(servers[0].url, servers[0].accessToken, 0, 1, 'createdAt') | 252 | const res = await getAccountBlocklistByAccount(servers[0].url, servers[0].accessToken, 0, 1, 'createdAt') |
@@ -269,6 +277,61 @@ describe('Test blocklist', function () { | |||
269 | } | 277 | } |
270 | }) | 278 | }) |
271 | 279 | ||
280 | it('Should not allow a remote blocked user to comment my videos', async function () { | ||
281 | this.timeout(60000) | ||
282 | |||
283 | { | ||
284 | await addVideoCommentThread(servers[1].url, userToken2, videoUUID3, 'comment user 2') | ||
285 | await waitJobs(servers) | ||
286 | |||
287 | await addVideoCommentThread(servers[0].url, servers[0].accessToken, videoUUID3, 'uploader') | ||
288 | await waitJobs(servers) | ||
289 | |||
290 | const commentId = await findCommentId(servers[1].url, videoUUID3, 'uploader') | ||
291 | const message = 'reply by user 2' | ||
292 | const resReply = await addVideoCommentReply(servers[1].url, userToken2, videoUUID3, commentId, message) | ||
293 | await addVideoCommentReply(servers[1].url, servers[1].accessToken, videoUUID3, resReply.body.comment.id, 'another reply') | ||
294 | |||
295 | await waitJobs(servers) | ||
296 | } | ||
297 | |||
298 | // Server 2 has all the comments | ||
299 | { | ||
300 | const resThreads = await getVideoCommentThreads(servers[1].url, videoUUID3, 0, 25, '-createdAt') | ||
301 | const threads: VideoComment[] = resThreads.body.data | ||
302 | |||
303 | expect(threads).to.have.lengthOf(2) | ||
304 | expect(threads[0].text).to.equal('uploader') | ||
305 | expect(threads[1].text).to.equal('comment user 2') | ||
306 | |||
307 | const resReplies = await getVideoThreadComments(servers[1].url, videoUUID3, threads[0].id) | ||
308 | |||
309 | const tree: VideoCommentThreadTree = resReplies.body | ||
310 | expect(tree.children).to.have.lengthOf(1) | ||
311 | expect(tree.children[0].comment.text).to.equal('reply by user 2') | ||
312 | expect(tree.children[0].children).to.have.lengthOf(1) | ||
313 | expect(tree.children[0].children[0].comment.text).to.equal('another reply') | ||
314 | } | ||
315 | |||
316 | // Server 1 and 3 should only have uploader comments | ||
317 | for (const server of [ servers[0], servers[2] ]) { | ||
318 | const resThreads = await getVideoCommentThreads(server.url, videoUUID3, 0, 25, '-createdAt') | ||
319 | const threads: VideoComment[] = resThreads.body.data | ||
320 | |||
321 | expect(threads).to.have.lengthOf(1) | ||
322 | expect(threads[0].text).to.equal('uploader') | ||
323 | |||
324 | const resReplies = await getVideoThreadComments(server.url, videoUUID3, threads[0].id) | ||
325 | |||
326 | const tree: VideoCommentThreadTree = resReplies.body | ||
327 | if (server.serverNumber === 1) { | ||
328 | expect(tree.children).to.have.lengthOf(0) | ||
329 | } else { | ||
330 | expect(tree.children).to.have.lengthOf(1) | ||
331 | } | ||
332 | } | ||
333 | }) | ||
334 | |||
272 | it('Should unblock the remote account', async function () { | 335 | it('Should unblock the remote account', async function () { |
273 | await removeAccountFromAccountBlocklist(servers[0].url, servers[0].accessToken, 'user2@localhost:' + servers[1].port) | 336 | await removeAccountFromAccountBlocklist(servers[0].url, servers[0].accessToken, 'user2@localhost:' + servers[1].port) |
274 | }) | 337 | }) |
@@ -277,12 +340,37 @@ describe('Test blocklist', function () { | |||
277 | const res = await getVideosListWithToken(servers[0].url, servers[0].accessToken) | 340 | const res = await getVideosListWithToken(servers[0].url, servers[0].accessToken) |
278 | 341 | ||
279 | const videos: Video[] = res.body.data | 342 | const videos: Video[] = res.body.data |
280 | expect(videos).to.have.lengthOf(3) | 343 | expect(videos).to.have.lengthOf(4) |
281 | 344 | ||
282 | const v = videos.find(v => v.name === 'video user 2') | 345 | const v = videos.find(v => v.name === 'video user 2') |
283 | expect(v).not.to.be.undefined | 346 | expect(v).not.to.be.undefined |
284 | }) | 347 | }) |
285 | 348 | ||
349 | it('Should display its comments on my video', async function () { | ||
350 | for (const server of servers) { | ||
351 | const resThreads = await getVideoCommentThreads(server.url, videoUUID3, 0, 25, '-createdAt') | ||
352 | const threads: VideoComment[] = resThreads.body.data | ||
353 | |||
354 | // Server 3 should not have 2 comment threads, because server 1 did not forward the server 2 comment | ||
355 | if (server.serverNumber === 3) { | ||
356 | expect(threads).to.have.lengthOf(1) | ||
357 | continue | ||
358 | } | ||
359 | |||
360 | expect(threads).to.have.lengthOf(2) | ||
361 | expect(threads[0].text).to.equal('uploader') | ||
362 | expect(threads[1].text).to.equal('comment user 2') | ||
363 | |||
364 | const resReplies = await getVideoThreadComments(server.url, videoUUID3, threads[0].id) | ||
365 | |||
366 | const tree: VideoCommentThreadTree = resReplies.body | ||
367 | expect(tree.children).to.have.lengthOf(1) | ||
368 | expect(tree.children[0].comment.text).to.equal('reply by user 2') | ||
369 | expect(tree.children[0].children).to.have.lengthOf(1) | ||
370 | expect(tree.children[0].children[0].comment.text).to.equal('another reply') | ||
371 | } | ||
372 | }) | ||
373 | |||
286 | it('Should unblock the local account', async function () { | 374 | it('Should unblock the local account', async function () { |
287 | await removeAccountFromAccountBlocklist(servers[0].url, servers[0].accessToken, 'user1') | 375 | await removeAccountFromAccountBlocklist(servers[0].url, servers[0].accessToken, 'user1') |
288 | }) | 376 | }) |
@@ -328,7 +416,7 @@ describe('Test blocklist', function () { | |||
328 | const res = await getVideosListWithToken(servers[0].url, servers[0].accessToken) | 416 | const res = await getVideosListWithToken(servers[0].url, servers[0].accessToken) |
329 | 417 | ||
330 | const videos: Video[] = res.body.data | 418 | const videos: Video[] = res.body.data |
331 | expect(videos).to.have.lengthOf(2) | 419 | expect(videos).to.have.lengthOf(3) |
332 | 420 | ||
333 | const v1 = videos.find(v => v.name === 'video user 2') | 421 | const v1 = videos.find(v => v.name === 'video user 2') |
334 | const v2 = videos.find(v => v.name === 'video server 2') | 422 | const v2 = videos.find(v => v.name === 'video server 2') |
@@ -442,7 +530,7 @@ describe('Test blocklist', function () { | |||
442 | const res = await getVideosListWithToken(servers[0].url, token) | 530 | const res = await getVideosListWithToken(servers[0].url, token) |
443 | 531 | ||
444 | const videos: Video[] = res.body.data | 532 | const videos: Video[] = res.body.data |
445 | expect(videos).to.have.lengthOf(3) | 533 | expect(videos).to.have.lengthOf(4) |
446 | 534 | ||
447 | const v = videos.find(v => v.name === 'video user 2') | 535 | const v = videos.find(v => v.name === 'video user 2') |
448 | expect(v).to.be.undefined | 536 | expect(v).to.be.undefined |
@@ -458,7 +546,7 @@ describe('Test blocklist', function () { | |||
458 | const res = await getVideosListWithToken(servers[0].url, token) | 546 | const res = await getVideosListWithToken(servers[0].url, token) |
459 | 547 | ||
460 | const videos: Video[] = res.body.data | 548 | const videos: Video[] = res.body.data |
461 | expect(videos).to.have.lengthOf(2) | 549 | expect(videos).to.have.lengthOf(3) |
462 | 550 | ||
463 | const v = videos.find(v => v.name === 'video user 1') | 551 | const v = videos.find(v => v.name === 'video user 1') |
464 | expect(v).to.be.undefined | 552 | expect(v).to.be.undefined |
@@ -545,7 +633,7 @@ describe('Test blocklist', function () { | |||
545 | const res = await getVideosListWithToken(servers[0].url, token) | 633 | const res = await getVideosListWithToken(servers[0].url, token) |
546 | 634 | ||
547 | const videos: Video[] = res.body.data | 635 | const videos: Video[] = res.body.data |
548 | expect(videos).to.have.lengthOf(3) | 636 | expect(videos).to.have.lengthOf(4) |
549 | 637 | ||
550 | const v = videos.find(v => v.name === 'video user 2') | 638 | const v = videos.find(v => v.name === 'video user 2') |
551 | expect(v).not.to.be.undefined | 639 | expect(v).not.to.be.undefined |
@@ -606,7 +694,7 @@ describe('Test blocklist', function () { | |||
606 | 694 | ||
607 | for (const res of [ res1, res2 ]) { | 695 | for (const res of [ res1, res2 ]) { |
608 | const videos: Video[] = res.body.data | 696 | const videos: Video[] = res.body.data |
609 | expect(videos).to.have.lengthOf(2) | 697 | expect(videos).to.have.lengthOf(3) |
610 | 698 | ||
611 | const v1 = videos.find(v => v.name === 'video user 2') | 699 | const v1 = videos.find(v => v.name === 'video user 2') |
612 | const v2 = videos.find(v => v.name === 'video server 2') | 700 | const v2 = videos.find(v => v.name === 'video server 2') |
@@ -631,7 +719,7 @@ describe('Test blocklist', function () { | |||
631 | }) | 719 | }) |
632 | 720 | ||
633 | it('Should not have notification from blocked instances by instance', async function () { | 721 | it('Should not have notification from blocked instances by instance', async function () { |
634 | this.timeout(20000) | 722 | this.timeout(50000) |
635 | 723 | ||
636 | { | 724 | { |
637 | const comment = { server: servers[1], token: userToken2, videoUUID: videoUUID1, text: 'hidden comment' } | 725 | const comment = { server: servers[1], token: userToken2, videoUUID: videoUUID1, text: 'hidden comment' } |
@@ -647,6 +735,24 @@ describe('Test blocklist', function () { | |||
647 | } | 735 | } |
648 | await checkCommentNotification(servers[0], comment, 'absence') | 736 | await checkCommentNotification(servers[0], comment, 'absence') |
649 | } | 737 | } |
738 | |||
739 | { | ||
740 | const now = new Date() | ||
741 | await unfollow(servers[1].url, servers[1].accessToken, servers[0]) | ||
742 | await waitJobs(servers) | ||
743 | await follow(servers[1].url, [ servers[0].host ], servers[1].accessToken) | ||
744 | |||
745 | await waitJobs(servers) | ||
746 | |||
747 | const res = await getUserNotifications(servers[0].url, servers[0].accessToken, 0, 30) | ||
748 | const commentNotifications = (res.body.data as UserNotification[]) | ||
749 | .filter(n => { | ||
750 | return n.type === UserNotificationType.NEW_INSTANCE_FOLLOWER && | ||
751 | n.createdAt >= now.toISOString() | ||
752 | }) | ||
753 | |||
754 | expect(commentNotifications).to.have.lengthOf(0) | ||
755 | } | ||
650 | }) | 756 | }) |
651 | 757 | ||
652 | it('Should list blocked servers', async function () { | 758 | it('Should list blocked servers', async function () { |
@@ -678,7 +784,7 @@ describe('Test blocklist', function () { | |||
678 | }) | 784 | }) |
679 | 785 | ||
680 | it('Should have notification from unblocked instances', async function () { | 786 | it('Should have notification from unblocked instances', async function () { |
681 | this.timeout(20000) | 787 | this.timeout(50000) |
682 | 788 | ||
683 | { | 789 | { |
684 | const comment = { server: servers[1], token: userToken2, videoUUID: videoUUID1, text: 'displayed comment' } | 790 | const comment = { server: servers[1], token: userToken2, videoUUID: videoUUID1, text: 'displayed comment' } |
@@ -694,6 +800,24 @@ describe('Test blocklist', function () { | |||
694 | } | 800 | } |
695 | await checkCommentNotification(servers[0], comment, 'presence') | 801 | await checkCommentNotification(servers[0], comment, 'presence') |
696 | } | 802 | } |
803 | |||
804 | { | ||
805 | const now = new Date() | ||
806 | await unfollow(servers[1].url, servers[1].accessToken, servers[0]) | ||
807 | await waitJobs(servers) | ||
808 | await follow(servers[1].url, [ servers[0].host ], servers[1].accessToken) | ||
809 | |||
810 | await waitJobs(servers) | ||
811 | |||
812 | const res = await getUserNotifications(servers[0].url, servers[0].accessToken, 0, 30) | ||
813 | const commentNotifications = (res.body.data as UserNotification[]) | ||
814 | .filter(n => { | ||
815 | return n.type === UserNotificationType.NEW_INSTANCE_FOLLOWER && | ||
816 | n.createdAt >= now.toISOString() | ||
817 | }) | ||
818 | |||
819 | expect(commentNotifications).to.have.lengthOf(1) | ||
820 | } | ||
697 | }) | 821 | }) |
698 | }) | 822 | }) |
699 | }) | 823 | }) |