aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/tests/api/moderation
diff options
context:
space:
mode:
Diffstat (limited to 'server/tests/api/moderation')
-rw-r--r--server/tests/api/moderation/abuses.ts384
-rw-r--r--server/tests/api/moderation/blocklist.ts828
-rw-r--r--server/tests/api/moderation/index.ts2
3 files changed, 1214 insertions, 0 deletions
diff --git a/server/tests/api/moderation/abuses.ts b/server/tests/api/moderation/abuses.ts
new file mode 100644
index 000000000..28c5a5531
--- /dev/null
+++ b/server/tests/api/moderation/abuses.ts
@@ -0,0 +1,384 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import 'mocha'
4import * as chai from 'chai'
5import { Abuse, AbusePredefinedReasonsString, AbuseState } from '@shared/models'
6import {
7 cleanupTests,
8 createUser,
9 deleteVideoAbuse,
10 flushAndRunMultipleServers,
11 getVideoAbusesList,
12 getVideosList,
13 removeVideo,
14 reportVideoAbuse,
15 ServerInfo,
16 setAccessTokensToServers,
17 updateVideoAbuse,
18 uploadVideo,
19 userLogin
20} from '../../../../shared/extra-utils/index'
21import { doubleFollow } from '../../../../shared/extra-utils/server/follows'
22import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
23import {
24 addAccountToServerBlocklist,
25 addServerToServerBlocklist,
26 removeAccountFromServerBlocklist,
27 removeServerFromServerBlocklist
28} from '../../../../shared/extra-utils/users/blocklist'
29
30const expect = chai.expect
31
32describe('Test abuses', function () {
33 let servers: ServerInfo[] = []
34 let abuseServer2: Abuse
35
36 before(async function () {
37 this.timeout(50000)
38
39 // Run servers
40 servers = await flushAndRunMultipleServers(2)
41
42 // Get the access tokens
43 await setAccessTokensToServers(servers)
44
45 // Server 1 and server 2 follow each other
46 await doubleFollow(servers[0], servers[1])
47
48 // Upload some videos on each servers
49 const video1Attributes = {
50 name: 'my super name for server 1',
51 description: 'my super description for server 1'
52 }
53 await uploadVideo(servers[0].url, servers[0].accessToken, video1Attributes)
54
55 const video2Attributes = {
56 name: 'my super name for server 2',
57 description: 'my super description for server 2'
58 }
59 await uploadVideo(servers[1].url, servers[1].accessToken, video2Attributes)
60
61 // Wait videos propagation, server 2 has transcoding enabled
62 await waitJobs(servers)
63
64 const res = await getVideosList(servers[0].url)
65 const videos = res.body.data
66
67 expect(videos.length).to.equal(2)
68
69 servers[0].video = videos.find(video => video.name === 'my super name for server 1')
70 servers[1].video = videos.find(video => video.name === 'my super name for server 2')
71 })
72
73 it('Should not have video abuses', async function () {
74 const res = await getVideoAbusesList({ url: servers[0].url, token: servers[0].accessToken })
75
76 expect(res.body.total).to.equal(0)
77 expect(res.body.data).to.be.an('array')
78 expect(res.body.data.length).to.equal(0)
79 })
80
81 it('Should report abuse on a local video', async function () {
82 this.timeout(15000)
83
84 const reason = 'my super bad reason'
85 await reportVideoAbuse(servers[0].url, servers[0].accessToken, servers[0].video.id, reason)
86
87 // We wait requests propagation, even if the server 1 is not supposed to make a request to server 2
88 await waitJobs(servers)
89 })
90
91 it('Should have 1 video abuses on server 1 and 0 on server 2', async function () {
92 const res1 = await getVideoAbusesList({ url: servers[0].url, token: servers[0].accessToken })
93
94 expect(res1.body.total).to.equal(1)
95 expect(res1.body.data).to.be.an('array')
96 expect(res1.body.data.length).to.equal(1)
97
98 const abuse: Abuse = res1.body.data[0]
99 expect(abuse.reason).to.equal('my super bad reason')
100 expect(abuse.reporterAccount.name).to.equal('root')
101 expect(abuse.reporterAccount.host).to.equal('localhost:' + servers[0].port)
102 expect(abuse.video.id).to.equal(servers[0].video.id)
103 expect(abuse.video.channel).to.exist
104 expect(abuse.count).to.equal(1)
105 expect(abuse.nth).to.equal(1)
106 expect(abuse.countReportsForReporter).to.equal(1)
107 expect(abuse.countReportsForReportee).to.equal(1)
108
109 const res2 = await getVideoAbusesList({ url: servers[1].url, token: servers[1].accessToken })
110 expect(res2.body.total).to.equal(0)
111 expect(res2.body.data).to.be.an('array')
112 expect(res2.body.data.length).to.equal(0)
113 })
114
115 it('Should report abuse on a remote video', async function () {
116 this.timeout(10000)
117
118 const reason = 'my super bad reason 2'
119 await reportVideoAbuse(servers[0].url, servers[0].accessToken, servers[1].video.id, reason)
120
121 // We wait requests propagation
122 await waitJobs(servers)
123 })
124
125 it('Should have 2 video abuses on server 1 and 1 on server 2', async function () {
126 const res1 = await getVideoAbusesList({ url: servers[0].url, token: servers[0].accessToken })
127 expect(res1.body.total).to.equal(2)
128 expect(res1.body.data).to.be.an('array')
129 expect(res1.body.data.length).to.equal(2)
130
131 const abuse1: Abuse = res1.body.data[0]
132 expect(abuse1.reason).to.equal('my super bad reason')
133 expect(abuse1.reporterAccount.name).to.equal('root')
134 expect(abuse1.reporterAccount.host).to.equal('localhost:' + servers[0].port)
135 expect(abuse1.video.id).to.equal(servers[0].video.id)
136 expect(abuse1.state.id).to.equal(AbuseState.PENDING)
137 expect(abuse1.state.label).to.equal('Pending')
138 expect(abuse1.moderationComment).to.be.null
139 expect(abuse1.count).to.equal(1)
140 expect(abuse1.nth).to.equal(1)
141
142 const abuse2: Abuse = res1.body.data[1]
143 expect(abuse2.reason).to.equal('my super bad reason 2')
144 expect(abuse2.reporterAccount.name).to.equal('root')
145 expect(abuse2.reporterAccount.host).to.equal('localhost:' + servers[0].port)
146 expect(abuse2.video.id).to.equal(servers[1].video.id)
147 expect(abuse2.state.id).to.equal(AbuseState.PENDING)
148 expect(abuse2.state.label).to.equal('Pending')
149 expect(abuse2.moderationComment).to.be.null
150
151 const res2 = await getVideoAbusesList({ url: servers[1].url, token: servers[1].accessToken })
152 expect(res2.body.total).to.equal(1)
153 expect(res2.body.data).to.be.an('array')
154 expect(res2.body.data.length).to.equal(1)
155
156 abuseServer2 = res2.body.data[0]
157 expect(abuseServer2.reason).to.equal('my super bad reason 2')
158 expect(abuseServer2.reporterAccount.name).to.equal('root')
159 expect(abuseServer2.reporterAccount.host).to.equal('localhost:' + servers[0].port)
160 expect(abuseServer2.state.id).to.equal(AbuseState.PENDING)
161 expect(abuseServer2.state.label).to.equal('Pending')
162 expect(abuseServer2.moderationComment).to.be.null
163 })
164
165 it('Should update the state of a video abuse', async function () {
166 const body = { state: AbuseState.REJECTED }
167 await updateVideoAbuse(servers[1].url, servers[1].accessToken, abuseServer2.video.uuid, abuseServer2.id, body)
168
169 const res = await getVideoAbusesList({ url: servers[1].url, token: servers[1].accessToken })
170 expect(res.body.data[0].state.id).to.equal(AbuseState.REJECTED)
171 })
172
173 it('Should add a moderation comment', async function () {
174 const body = { state: AbuseState.ACCEPTED, moderationComment: 'It is valid' }
175 await updateVideoAbuse(servers[1].url, servers[1].accessToken, abuseServer2.video.uuid, abuseServer2.id, body)
176
177 const res = await getVideoAbusesList({ url: servers[1].url, token: servers[1].accessToken })
178 expect(res.body.data[0].state.id).to.equal(AbuseState.ACCEPTED)
179 expect(res.body.data[0].moderationComment).to.equal('It is valid')
180 })
181
182 it('Should hide video abuses from blocked accounts', async function () {
183 this.timeout(10000)
184
185 {
186 await reportVideoAbuse(servers[1].url, servers[1].accessToken, servers[0].video.uuid, 'will mute this')
187 await waitJobs(servers)
188
189 const res = await getVideoAbusesList({ url: servers[0].url, token: servers[0].accessToken })
190 expect(res.body.total).to.equal(3)
191 }
192
193 const accountToBlock = 'root@localhost:' + servers[1].port
194
195 {
196 await addAccountToServerBlocklist(servers[0].url, servers[0].accessToken, accountToBlock)
197
198 const res = await getVideoAbusesList({ url: servers[0].url, token: servers[0].accessToken })
199 expect(res.body.total).to.equal(2)
200
201 const abuse = res.body.data.find(a => a.reason === 'will mute this')
202 expect(abuse).to.be.undefined
203 }
204
205 {
206 await removeAccountFromServerBlocklist(servers[0].url, servers[0].accessToken, accountToBlock)
207
208 const res = await getVideoAbusesList({ url: servers[0].url, token: servers[0].accessToken })
209 expect(res.body.total).to.equal(3)
210 }
211 })
212
213 it('Should hide video abuses from blocked servers', async function () {
214 const serverToBlock = servers[1].host
215
216 {
217 await addServerToServerBlocklist(servers[0].url, servers[0].accessToken, servers[1].host)
218
219 const res = await getVideoAbusesList({ url: servers[0].url, token: servers[0].accessToken })
220 expect(res.body.total).to.equal(2)
221
222 const abuse = res.body.data.find(a => a.reason === 'will mute this')
223 expect(abuse).to.be.undefined
224 }
225
226 {
227 await removeServerFromServerBlocklist(servers[0].url, servers[0].accessToken, serverToBlock)
228
229 const res = await getVideoAbusesList({ url: servers[0].url, token: servers[0].accessToken })
230 expect(res.body.total).to.equal(3)
231 }
232 })
233
234 it('Should keep the video abuse when deleting the video', async function () {
235 this.timeout(10000)
236
237 await removeVideo(servers[1].url, servers[1].accessToken, abuseServer2.video.uuid)
238
239 await waitJobs(servers)
240
241 const res = await getVideoAbusesList({ url: servers[1].url, token: servers[1].accessToken })
242 expect(res.body.total).to.equal(2, "wrong number of videos returned")
243 expect(res.body.data.length).to.equal(2, "wrong number of videos returned")
244 expect(res.body.data[0].id).to.equal(abuseServer2.id, "wrong origin server id for first video")
245
246 const abuse: Abuse = res.body.data[0]
247 expect(abuse.video.id).to.equal(abuseServer2.video.id, "wrong video id")
248 expect(abuse.video.channel).to.exist
249 expect(abuse.video.deleted).to.be.true
250 })
251
252 it('Should include counts of reports from reporter and reportee', async function () {
253 this.timeout(10000)
254
255 // register a second user to have two reporters/reportees
256 const user = { username: 'user2', password: 'password' }
257 await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, ...user })
258 const userAccessToken = await userLogin(servers[0], user)
259
260 // upload a third video via this user
261 const video3Attributes = {
262 name: 'my second super name for server 1',
263 description: 'my second super description for server 1'
264 }
265 await uploadVideo(servers[0].url, userAccessToken, video3Attributes)
266
267 const res1 = await getVideosList(servers[0].url)
268 const videos = res1.body.data
269 const video3 = videos.find(video => video.name === 'my second super name for server 1')
270
271 // resume with the test
272 const reason3 = 'my super bad reason 3'
273 await reportVideoAbuse(servers[0].url, servers[0].accessToken, video3.id, reason3)
274 const reason4 = 'my super bad reason 4'
275 await reportVideoAbuse(servers[0].url, userAccessToken, servers[0].video.id, reason4)
276
277 const res2 = await getVideoAbusesList({ url: servers[0].url, token: servers[0].accessToken })
278
279 {
280 for (const abuse of res2.body.data as Abuse[]) {
281 if (abuse.video.id === video3.id) {
282 expect(abuse.count).to.equal(1, "wrong reports count for video 3")
283 expect(abuse.nth).to.equal(1, "wrong report position in report list for video 3")
284 expect(abuse.countReportsForReportee).to.equal(1, "wrong reports count for reporter on video 3 abuse")
285 expect(abuse.countReportsForReporter).to.equal(3, "wrong reports count for reportee on video 3 abuse")
286 }
287 if (abuse.video.id === servers[0].video.id) {
288 expect(abuse.countReportsForReportee).to.equal(3, "wrong reports count for reporter on video 1 abuse")
289 }
290 }
291 }
292 })
293
294 it('Should list predefined reasons as well as timestamps for the reported video', async function () {
295 this.timeout(10000)
296
297 const reason5 = 'my super bad reason 5'
298 const predefinedReasons5: AbusePredefinedReasonsString[] = [ 'violentOrRepulsive', 'captions' ]
299 const createdAbuse = (await reportVideoAbuse(
300 servers[0].url,
301 servers[0].accessToken,
302 servers[0].video.id,
303 reason5,
304 predefinedReasons5,
305 1,
306 5
307 )).body.abuse
308
309 const res = await getVideoAbusesList({ url: servers[0].url, token: servers[0].accessToken })
310
311 {
312 const abuse = (res.body.data as Abuse[]).find(a => a.id === createdAbuse.id)
313 expect(abuse.reason).to.equals(reason5)
314 expect(abuse.predefinedReasons).to.deep.equals(predefinedReasons5, "predefined reasons do not match the one reported")
315 expect(abuse.video.startAt).to.equal(1, "starting timestamp doesn't match the one reported")
316 expect(abuse.video.endAt).to.equal(5, "ending timestamp doesn't match the one reported")
317 }
318 })
319
320 it('Should delete the video abuse', async function () {
321 this.timeout(10000)
322
323 await deleteVideoAbuse(servers[1].url, servers[1].accessToken, abuseServer2.video.uuid, abuseServer2.id)
324
325 await waitJobs(servers)
326
327 {
328 const res = await getVideoAbusesList({ url: servers[1].url, token: servers[1].accessToken })
329 expect(res.body.total).to.equal(1)
330 expect(res.body.data.length).to.equal(1)
331 expect(res.body.data[0].id).to.not.equal(abuseServer2.id)
332 }
333
334 {
335 const res = await getVideoAbusesList({ url: servers[0].url, token: servers[0].accessToken })
336 expect(res.body.total).to.equal(6)
337 }
338 })
339
340 it('Should list and filter video abuses', async function () {
341 async function list (query: Omit<Parameters<typeof getVideoAbusesList>[0], 'url' | 'token'>) {
342 const options = {
343 url: servers[0].url,
344 token: servers[0].accessToken
345 }
346
347 Object.assign(options, query)
348
349 const res = await getVideoAbusesList(options)
350
351 return res.body.data as Abuse[]
352 }
353
354 expect(await list({ id: 56 })).to.have.lengthOf(0)
355 expect(await list({ id: 1 })).to.have.lengthOf(1)
356
357 expect(await list({ search: 'my super name for server 1' })).to.have.lengthOf(4)
358 expect(await list({ search: 'aaaaaaaaaaaaaaaaaaaaaaaaaa' })).to.have.lengthOf(0)
359
360 expect(await list({ searchVideo: 'my second super name for server 1' })).to.have.lengthOf(1)
361
362 expect(await list({ searchVideoChannel: 'root' })).to.have.lengthOf(4)
363 expect(await list({ searchVideoChannel: 'aaaa' })).to.have.lengthOf(0)
364
365 expect(await list({ searchReporter: 'user2' })).to.have.lengthOf(1)
366 expect(await list({ searchReporter: 'root' })).to.have.lengthOf(5)
367
368 expect(await list({ searchReportee: 'root' })).to.have.lengthOf(5)
369 expect(await list({ searchReportee: 'aaaa' })).to.have.lengthOf(0)
370
371 expect(await list({ videoIs: 'deleted' })).to.have.lengthOf(1)
372 expect(await list({ videoIs: 'blacklisted' })).to.have.lengthOf(0)
373
374 expect(await list({ state: AbuseState.ACCEPTED })).to.have.lengthOf(0)
375 expect(await list({ state: AbuseState.PENDING })).to.have.lengthOf(6)
376
377 expect(await list({ predefinedReason: 'violentOrRepulsive' })).to.have.lengthOf(1)
378 expect(await list({ predefinedReason: 'serverRules' })).to.have.lengthOf(0)
379 })
380
381 after(async function () {
382 await cleanupTests(servers)
383 })
384})
diff --git a/server/tests/api/moderation/blocklist.ts b/server/tests/api/moderation/blocklist.ts
new file mode 100644
index 000000000..8c9107a50
--- /dev/null
+++ b/server/tests/api/moderation/blocklist.ts
@@ -0,0 +1,828 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import * as chai from 'chai'
4import 'mocha'
5import { AccountBlock, ServerBlock, Video, UserNotification, UserNotificationType } from '../../../../shared/index'
6import {
7 cleanupTests,
8 createUser,
9 deleteVideoComment,
10 doubleFollow,
11 flushAndRunMultipleServers,
12 ServerInfo,
13 uploadVideo,
14 userLogin,
15 follow,
16 unfollow
17} from '../../../../shared/extra-utils/index'
18import { setAccessTokensToServers } from '../../../../shared/extra-utils/users/login'
19import { getVideosList, getVideosListWithToken } from '../../../../shared/extra-utils/videos/videos'
20import {
21 addVideoCommentReply,
22 addVideoCommentThread,
23 getVideoCommentThreads,
24 getVideoThreadComments,
25 findCommentId
26} from '../../../../shared/extra-utils/videos/video-comments'
27import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
28import { VideoComment, VideoCommentThreadTree } from '../../../../shared/models/videos/video-comment.model'
29import {
30 addAccountToAccountBlocklist,
31 addAccountToServerBlocklist,
32 addServerToAccountBlocklist,
33 addServerToServerBlocklist,
34 getAccountBlocklistByAccount,
35 getAccountBlocklistByServer,
36 getServerBlocklistByAccount,
37 getServerBlocklistByServer,
38 removeAccountFromAccountBlocklist,
39 removeAccountFromServerBlocklist,
40 removeServerFromAccountBlocklist,
41 removeServerFromServerBlocklist
42} from '../../../../shared/extra-utils/users/blocklist'
43import { getUserNotifications } from '../../../../shared/extra-utils/users/user-notifications'
44
45const expect = chai.expect
46
47async function checkAllVideos (url: string, token: string) {
48 {
49 const res = await getVideosListWithToken(url, token)
50
51 expect(res.body.data).to.have.lengthOf(5)
52 }
53
54 {
55 const res = await getVideosList(url)
56
57 expect(res.body.data).to.have.lengthOf(5)
58 }
59}
60
61async function checkAllComments (url: string, token: string, videoUUID: string) {
62 const resThreads = await getVideoCommentThreads(url, videoUUID, 0, 25, '-createdAt', token)
63
64 const allThreads: VideoComment[] = resThreads.body.data
65 const threads = allThreads.filter(t => t.isDeleted === false)
66 expect(threads).to.have.lengthOf(2)
67
68 for (const thread of threads) {
69 const res = await getVideoThreadComments(url, videoUUID, thread.id, token)
70
71 const tree: VideoCommentThreadTree = res.body
72 expect(tree.children).to.have.lengthOf(1)
73 }
74}
75
76async function checkCommentNotification (
77 mainServer: ServerInfo,
78 comment: { server: ServerInfo, token: string, videoUUID: string, text: string },
79 check: 'presence' | 'absence'
80) {
81 const resComment = await addVideoCommentThread(comment.server.url, comment.token, comment.videoUUID, comment.text)
82 const created = resComment.body.comment as VideoComment
83 const threadId = created.id
84 const createdAt = created.createdAt
85
86 await waitJobs([ mainServer, comment.server ])
87
88 const res = await getUserNotifications(mainServer.url, mainServer.accessToken, 0, 30)
89 const commentNotifications = (res.body.data as UserNotification[])
90 .filter(n => n.comment && n.comment.video.uuid === comment.videoUUID && n.createdAt >= createdAt)
91
92 if (check === 'presence') expect(commentNotifications).to.have.lengthOf(1)
93 else expect(commentNotifications).to.have.lengthOf(0)
94
95 await deleteVideoComment(comment.server.url, comment.token, comment.videoUUID, threadId)
96
97 await waitJobs([ mainServer, comment.server ])
98}
99
100describe('Test blocklist', function () {
101 let servers: ServerInfo[]
102 let videoUUID1: string
103 let videoUUID2: string
104 let videoUUID3: string
105 let userToken1: string
106 let userModeratorToken: string
107 let userToken2: string
108
109 before(async function () {
110 this.timeout(60000)
111
112 servers = await flushAndRunMultipleServers(3)
113 await setAccessTokensToServers(servers)
114
115 {
116 const user = { username: 'user1', password: 'password' }
117 await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, username: user.username, password: user.password })
118
119 userToken1 = await userLogin(servers[0], user)
120 await uploadVideo(servers[0].url, userToken1, { name: 'video user 1' })
121 }
122
123 {
124 const user = { username: 'moderator', password: 'password' }
125 await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, username: user.username, password: user.password })
126
127 userModeratorToken = await userLogin(servers[0], user)
128 }
129
130 {
131 const user = { username: 'user2', password: 'password' }
132 await createUser({ url: servers[1].url, accessToken: servers[1].accessToken, username: user.username, password: user.password })
133
134 userToken2 = await userLogin(servers[1], user)
135 await uploadVideo(servers[1].url, userToken2, { name: 'video user 2' })
136 }
137
138 {
139 const res = await uploadVideo(servers[0].url, servers[0].accessToken, { name: 'video server 1' })
140 videoUUID1 = res.body.video.uuid
141 }
142
143 {
144 const res = await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'video server 2' })
145 videoUUID2 = res.body.video.uuid
146 }
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
153 await doubleFollow(servers[0], servers[1])
154 await doubleFollow(servers[0], servers[2])
155
156 {
157 const resComment = await addVideoCommentThread(servers[0].url, servers[0].accessToken, videoUUID1, 'comment root 1')
158 const resReply = await addVideoCommentReply(servers[0].url, userToken1, videoUUID1, resComment.body.comment.id, 'comment user 1')
159 await addVideoCommentReply(servers[0].url, servers[0].accessToken, videoUUID1, resReply.body.comment.id, 'comment root 1')
160 }
161
162 {
163 const resComment = await addVideoCommentThread(servers[0].url, userToken1, videoUUID1, 'comment user 1')
164 await addVideoCommentReply(servers[0].url, servers[0].accessToken, videoUUID1, resComment.body.comment.id, 'comment root 1')
165 }
166
167 await waitJobs(servers)
168 })
169
170 describe('User blocklist', function () {
171
172 describe('When managing account blocklist', function () {
173 it('Should list all videos', function () {
174 return checkAllVideos(servers[0].url, servers[0].accessToken)
175 })
176
177 it('Should list the comments', function () {
178 return checkAllComments(servers[0].url, servers[0].accessToken, videoUUID1)
179 })
180
181 it('Should block a remote account', async function () {
182 await addAccountToAccountBlocklist(servers[0].url, servers[0].accessToken, 'user2@localhost:' + servers[1].port)
183 })
184
185 it('Should hide its videos', async function () {
186 const res = await getVideosListWithToken(servers[0].url, servers[0].accessToken)
187
188 const videos: Video[] = res.body.data
189 expect(videos).to.have.lengthOf(4)
190
191 const v = videos.find(v => v.name === 'video user 2')
192 expect(v).to.be.undefined
193 })
194
195 it('Should block a local account', async function () {
196 await addAccountToAccountBlocklist(servers[0].url, servers[0].accessToken, 'user1')
197 })
198
199 it('Should hide its videos', async function () {
200 const res = await getVideosListWithToken(servers[0].url, servers[0].accessToken)
201
202 const videos: Video[] = res.body.data
203 expect(videos).to.have.lengthOf(3)
204
205 const v = videos.find(v => v.name === 'video user 1')
206 expect(v).to.be.undefined
207 })
208
209 it('Should hide its comments', async function () {
210 const resThreads = await getVideoCommentThreads(servers[0].url, videoUUID1, 0, 25, '-createdAt', servers[0].accessToken)
211
212 const threads: VideoComment[] = resThreads.body.data
213 expect(threads).to.have.lengthOf(1)
214 expect(threads[0].totalReplies).to.equal(0)
215
216 const t = threads.find(t => t.text === 'comment user 1')
217 expect(t).to.be.undefined
218
219 for (const thread of threads) {
220 const res = await getVideoThreadComments(servers[0].url, videoUUID1, thread.id, servers[0].accessToken)
221
222 const tree: VideoCommentThreadTree = res.body
223 expect(tree.children).to.have.lengthOf(0)
224 }
225 })
226
227 it('Should not have notifications from blocked accounts', async function () {
228 this.timeout(20000)
229
230 {
231 const comment = { server: servers[0], token: userToken1, videoUUID: videoUUID1, text: 'hidden comment' }
232 await checkCommentNotification(servers[0], comment, 'absence')
233 }
234
235 {
236 const comment = {
237 server: servers[0],
238 token: userToken1,
239 videoUUID: videoUUID2,
240 text: 'hello @root@localhost:' + servers[0].port
241 }
242 await checkCommentNotification(servers[0], comment, 'absence')
243 }
244 })
245
246 it('Should list all the videos with another user', async function () {
247 return checkAllVideos(servers[0].url, userToken1)
248 })
249
250 it('Should list blocked accounts', async function () {
251 {
252 const res = await getAccountBlocklistByAccount(servers[0].url, servers[0].accessToken, 0, 1, 'createdAt')
253 const blocks: AccountBlock[] = res.body.data
254
255 expect(res.body.total).to.equal(2)
256
257 const block = blocks[0]
258 expect(block.byAccount.displayName).to.equal('root')
259 expect(block.byAccount.name).to.equal('root')
260 expect(block.blockedAccount.displayName).to.equal('user2')
261 expect(block.blockedAccount.name).to.equal('user2')
262 expect(block.blockedAccount.host).to.equal('localhost:' + servers[1].port)
263 }
264
265 {
266 const res = await getAccountBlocklistByAccount(servers[0].url, servers[0].accessToken, 1, 2, 'createdAt')
267 const blocks: AccountBlock[] = res.body.data
268
269 expect(res.body.total).to.equal(2)
270
271 const block = blocks[0]
272 expect(block.byAccount.displayName).to.equal('root')
273 expect(block.byAccount.name).to.equal('root')
274 expect(block.blockedAccount.displayName).to.equal('user1')
275 expect(block.blockedAccount.name).to.equal('user1')
276 expect(block.blockedAccount.host).to.equal('localhost:' + servers[0].port)
277 }
278 })
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
335 it('Should unblock the remote account', async function () {
336 await removeAccountFromAccountBlocklist(servers[0].url, servers[0].accessToken, 'user2@localhost:' + servers[1].port)
337 })
338
339 it('Should display its videos', async function () {
340 const res = await getVideosListWithToken(servers[0].url, servers[0].accessToken)
341
342 const videos: Video[] = res.body.data
343 expect(videos).to.have.lengthOf(4)
344
345 const v = videos.find(v => v.name === 'video user 2')
346 expect(v).not.to.be.undefined
347 })
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
374 it('Should unblock the local account', async function () {
375 await removeAccountFromAccountBlocklist(servers[0].url, servers[0].accessToken, 'user1')
376 })
377
378 it('Should display its comments', function () {
379 return checkAllComments(servers[0].url, servers[0].accessToken, videoUUID1)
380 })
381
382 it('Should have a notification from a non blocked account', async function () {
383 this.timeout(20000)
384
385 {
386 const comment = { server: servers[1], token: userToken2, videoUUID: videoUUID1, text: 'displayed comment' }
387 await checkCommentNotification(servers[0], comment, 'presence')
388 }
389
390 {
391 const comment = {
392 server: servers[0],
393 token: userToken1,
394 videoUUID: videoUUID2,
395 text: 'hello @root@localhost:' + servers[0].port
396 }
397 await checkCommentNotification(servers[0], comment, 'presence')
398 }
399 })
400 })
401
402 describe('When managing server blocklist', function () {
403 it('Should list all videos', function () {
404 return checkAllVideos(servers[0].url, servers[0].accessToken)
405 })
406
407 it('Should list the comments', function () {
408 return checkAllComments(servers[0].url, servers[0].accessToken, videoUUID1)
409 })
410
411 it('Should block a remote server', async function () {
412 await addServerToAccountBlocklist(servers[0].url, servers[0].accessToken, 'localhost:' + servers[1].port)
413 })
414
415 it('Should hide its videos', async function () {
416 const res = await getVideosListWithToken(servers[0].url, servers[0].accessToken)
417
418 const videos: Video[] = res.body.data
419 expect(videos).to.have.lengthOf(3)
420
421 const v1 = videos.find(v => v.name === 'video user 2')
422 const v2 = videos.find(v => v.name === 'video server 2')
423
424 expect(v1).to.be.undefined
425 expect(v2).to.be.undefined
426 })
427
428 it('Should list all the videos with another user', async function () {
429 return checkAllVideos(servers[0].url, userToken1)
430 })
431
432 it('Should hide its comments', async function () {
433 this.timeout(10000)
434
435 const resThreads = await addVideoCommentThread(servers[1].url, userToken2, videoUUID1, 'hidden comment 2')
436 const threadId = resThreads.body.comment.id
437
438 await waitJobs(servers)
439
440 await checkAllComments(servers[0].url, servers[0].accessToken, videoUUID1)
441
442 await deleteVideoComment(servers[1].url, userToken2, videoUUID1, threadId)
443 })
444
445 it('Should not have notifications from blocked server', async function () {
446 this.timeout(20000)
447
448 {
449 const comment = { server: servers[1], token: userToken2, videoUUID: videoUUID1, text: 'hidden comment' }
450 await checkCommentNotification(servers[0], comment, 'absence')
451 }
452
453 {
454 const comment = {
455 server: servers[1],
456 token: userToken2,
457 videoUUID: videoUUID1,
458 text: 'hello @root@localhost:' + servers[0].port
459 }
460 await checkCommentNotification(servers[0], comment, 'absence')
461 }
462 })
463
464 it('Should list blocked servers', async function () {
465 const res = await getServerBlocklistByAccount(servers[0].url, servers[0].accessToken, 0, 1, 'createdAt')
466 const blocks: ServerBlock[] = res.body.data
467
468 expect(res.body.total).to.equal(1)
469
470 const block = blocks[0]
471 expect(block.byAccount.displayName).to.equal('root')
472 expect(block.byAccount.name).to.equal('root')
473 expect(block.blockedServer.host).to.equal('localhost:' + servers[1].port)
474 })
475
476 it('Should unblock the remote server', async function () {
477 await removeServerFromAccountBlocklist(servers[0].url, servers[0].accessToken, 'localhost:' + servers[1].port)
478 })
479
480 it('Should display its videos', function () {
481 return checkAllVideos(servers[0].url, servers[0].accessToken)
482 })
483
484 it('Should display its comments', function () {
485 return checkAllComments(servers[0].url, servers[0].accessToken, videoUUID1)
486 })
487
488 it('Should have notification from unblocked server', async function () {
489 this.timeout(20000)
490
491 {
492 const comment = { server: servers[1], token: userToken2, videoUUID: videoUUID1, text: 'displayed comment' }
493 await checkCommentNotification(servers[0], comment, 'presence')
494 }
495
496 {
497 const comment = {
498 server: servers[1],
499 token: userToken2,
500 videoUUID: videoUUID1,
501 text: 'hello @root@localhost:' + servers[0].port
502 }
503 await checkCommentNotification(servers[0], comment, 'presence')
504 }
505 })
506 })
507 })
508
509 describe('Server blocklist', function () {
510
511 describe('When managing account blocklist', function () {
512 it('Should list all videos', async function () {
513 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
514 await checkAllVideos(servers[0].url, token)
515 }
516 })
517
518 it('Should list the comments', async function () {
519 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
520 await checkAllComments(servers[0].url, token, videoUUID1)
521 }
522 })
523
524 it('Should block a remote account', async function () {
525 await addAccountToServerBlocklist(servers[0].url, servers[0].accessToken, 'user2@localhost:' + servers[1].port)
526 })
527
528 it('Should hide its videos', async function () {
529 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
530 const res = await getVideosListWithToken(servers[0].url, token)
531
532 const videos: Video[] = res.body.data
533 expect(videos).to.have.lengthOf(4)
534
535 const v = videos.find(v => v.name === 'video user 2')
536 expect(v).to.be.undefined
537 }
538 })
539
540 it('Should block a local account', async function () {
541 await addAccountToServerBlocklist(servers[0].url, servers[0].accessToken, 'user1')
542 })
543
544 it('Should hide its videos', async function () {
545 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
546 const res = await getVideosListWithToken(servers[0].url, token)
547
548 const videos: Video[] = res.body.data
549 expect(videos).to.have.lengthOf(3)
550
551 const v = videos.find(v => v.name === 'video user 1')
552 expect(v).to.be.undefined
553 }
554 })
555
556 it('Should hide its comments', async function () {
557 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
558 const resThreads = await getVideoCommentThreads(servers[0].url, videoUUID1, 0, 20, '-createdAt', token)
559
560 let threads: VideoComment[] = resThreads.body.data
561 threads = threads.filter(t => t.isDeleted === false)
562
563 expect(threads).to.have.lengthOf(1)
564 expect(threads[0].totalReplies).to.equal(0)
565
566 const t = threads.find(t => t.text === 'comment user 1')
567 expect(t).to.be.undefined
568
569 for (const thread of threads) {
570 const res = await getVideoThreadComments(servers[0].url, videoUUID1, thread.id, token)
571
572 const tree: VideoCommentThreadTree = res.body
573 expect(tree.children).to.have.lengthOf(0)
574 }
575 }
576 })
577
578 it('Should not have notification from blocked accounts by instance', async function () {
579 this.timeout(20000)
580
581 {
582 const comment = { server: servers[0], token: userToken1, videoUUID: videoUUID1, text: 'hidden comment' }
583 await checkCommentNotification(servers[0], comment, 'absence')
584 }
585
586 {
587 const comment = {
588 server: servers[1],
589 token: userToken2,
590 videoUUID: videoUUID1,
591 text: 'hello @root@localhost:' + servers[0].port
592 }
593 await checkCommentNotification(servers[0], comment, 'absence')
594 }
595 })
596
597 it('Should list blocked accounts', async function () {
598 {
599 const res = await getAccountBlocklistByServer(servers[0].url, servers[0].accessToken, 0, 1, 'createdAt')
600 const blocks: AccountBlock[] = res.body.data
601
602 expect(res.body.total).to.equal(2)
603
604 const block = blocks[0]
605 expect(block.byAccount.displayName).to.equal('peertube')
606 expect(block.byAccount.name).to.equal('peertube')
607 expect(block.blockedAccount.displayName).to.equal('user2')
608 expect(block.blockedAccount.name).to.equal('user2')
609 expect(block.blockedAccount.host).to.equal('localhost:' + servers[1].port)
610 }
611
612 {
613 const res = await getAccountBlocklistByServer(servers[0].url, servers[0].accessToken, 1, 2, 'createdAt')
614 const blocks: AccountBlock[] = res.body.data
615
616 expect(res.body.total).to.equal(2)
617
618 const block = blocks[0]
619 expect(block.byAccount.displayName).to.equal('peertube')
620 expect(block.byAccount.name).to.equal('peertube')
621 expect(block.blockedAccount.displayName).to.equal('user1')
622 expect(block.blockedAccount.name).to.equal('user1')
623 expect(block.blockedAccount.host).to.equal('localhost:' + servers[0].port)
624 }
625 })
626
627 it('Should unblock the remote account', async function () {
628 await removeAccountFromServerBlocklist(servers[0].url, servers[0].accessToken, 'user2@localhost:' + servers[1].port)
629 })
630
631 it('Should display its videos', async function () {
632 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
633 const res = await getVideosListWithToken(servers[0].url, token)
634
635 const videos: Video[] = res.body.data
636 expect(videos).to.have.lengthOf(4)
637
638 const v = videos.find(v => v.name === 'video user 2')
639 expect(v).not.to.be.undefined
640 }
641 })
642
643 it('Should unblock the local account', async function () {
644 await removeAccountFromServerBlocklist(servers[0].url, servers[0].accessToken, 'user1')
645 })
646
647 it('Should display its comments', async function () {
648 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
649 await checkAllComments(servers[0].url, token, videoUUID1)
650 }
651 })
652
653 it('Should have notifications from unblocked accounts', async function () {
654 this.timeout(20000)
655
656 {
657 const comment = { server: servers[0], token: userToken1, videoUUID: videoUUID1, text: 'displayed comment' }
658 await checkCommentNotification(servers[0], comment, 'presence')
659 }
660
661 {
662 const comment = {
663 server: servers[1],
664 token: userToken2,
665 videoUUID: videoUUID1,
666 text: 'hello @root@localhost:' + servers[0].port
667 }
668 await checkCommentNotification(servers[0], comment, 'presence')
669 }
670 })
671 })
672
673 describe('When managing server blocklist', function () {
674 it('Should list all videos', async function () {
675 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
676 await checkAllVideos(servers[0].url, token)
677 }
678 })
679
680 it('Should list the comments', async function () {
681 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
682 await checkAllComments(servers[0].url, token, videoUUID1)
683 }
684 })
685
686 it('Should block a remote server', async function () {
687 await addServerToServerBlocklist(servers[0].url, servers[0].accessToken, 'localhost:' + servers[1].port)
688 })
689
690 it('Should hide its videos', async function () {
691 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
692 const res1 = await getVideosList(servers[0].url)
693 const res2 = await getVideosListWithToken(servers[0].url, token)
694
695 for (const res of [ res1, res2 ]) {
696 const videos: Video[] = res.body.data
697 expect(videos).to.have.lengthOf(3)
698
699 const v1 = videos.find(v => v.name === 'video user 2')
700 const v2 = videos.find(v => v.name === 'video server 2')
701
702 expect(v1).to.be.undefined
703 expect(v2).to.be.undefined
704 }
705 }
706 })
707
708 it('Should hide its comments', async function () {
709 this.timeout(10000)
710
711 const resThreads = await addVideoCommentThread(servers[1].url, userToken2, videoUUID1, 'hidden comment 2')
712 const threadId = resThreads.body.comment.id
713
714 await waitJobs(servers)
715
716 await checkAllComments(servers[0].url, servers[0].accessToken, videoUUID1)
717
718 await deleteVideoComment(servers[1].url, userToken2, videoUUID1, threadId)
719 })
720
721 it('Should not have notification from blocked instances by instance', async function () {
722 this.timeout(50000)
723
724 {
725 const comment = { server: servers[1], token: userToken2, videoUUID: videoUUID1, text: 'hidden comment' }
726 await checkCommentNotification(servers[0], comment, 'absence')
727 }
728
729 {
730 const comment = {
731 server: servers[1],
732 token: userToken2,
733 videoUUID: videoUUID1,
734 text: 'hello @root@localhost:' + servers[0].port
735 }
736 await checkCommentNotification(servers[0], comment, 'absence')
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 }
756 })
757
758 it('Should list blocked servers', async function () {
759 const res = await getServerBlocklistByServer(servers[0].url, servers[0].accessToken, 0, 1, 'createdAt')
760 const blocks: ServerBlock[] = res.body.data
761
762 expect(res.body.total).to.equal(1)
763
764 const block = blocks[0]
765 expect(block.byAccount.displayName).to.equal('peertube')
766 expect(block.byAccount.name).to.equal('peertube')
767 expect(block.blockedServer.host).to.equal('localhost:' + servers[1].port)
768 })
769
770 it('Should unblock the remote server', async function () {
771 await removeServerFromServerBlocklist(servers[0].url, servers[0].accessToken, 'localhost:' + servers[1].port)
772 })
773
774 it('Should list all videos', async function () {
775 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
776 await checkAllVideos(servers[0].url, token)
777 }
778 })
779
780 it('Should list the comments', async function () {
781 for (const token of [ userModeratorToken, servers[0].accessToken ]) {
782 await checkAllComments(servers[0].url, token, videoUUID1)
783 }
784 })
785
786 it('Should have notification from unblocked instances', async function () {
787 this.timeout(50000)
788
789 {
790 const comment = { server: servers[1], token: userToken2, videoUUID: videoUUID1, text: 'displayed comment' }
791 await checkCommentNotification(servers[0], comment, 'presence')
792 }
793
794 {
795 const comment = {
796 server: servers[1],
797 token: userToken2,
798 videoUUID: videoUUID1,
799 text: 'hello @root@localhost:' + servers[0].port
800 }
801 await checkCommentNotification(servers[0], comment, 'presence')
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 }
821 })
822 })
823 })
824
825 after(async function () {
826 await cleanupTests(servers)
827 })
828})
diff --git a/server/tests/api/moderation/index.ts b/server/tests/api/moderation/index.ts
new file mode 100644
index 000000000..cb018d88e
--- /dev/null
+++ b/server/tests/api/moderation/index.ts
@@ -0,0 +1,2 @@
1export * from './abuses'
2export * from './blocklist'