Use video abuse filters on client side
[oweals/peertube.git] / server / tests / api / videos / video-abuse.ts
1 /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3 import * as chai from 'chai'
4 import 'mocha'
5 import { VideoAbuse, VideoAbuseState } from '../../../../shared/models/videos'
6 import {
7   cleanupTests,
8   deleteVideoAbuse,
9   flushAndRunMultipleServers,
10   getVideoAbusesList,
11   getVideosList,
12   reportVideoAbuse,
13   ServerInfo,
14   setAccessTokensToServers,
15   updateVideoAbuse,
16   uploadVideo,
17   removeVideo,
18   createUser,
19   userLogin
20 } from '../../../../shared/extra-utils/index'
21 import { doubleFollow } from '../../../../shared/extra-utils/server/follows'
22 import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
23 import {
24   addAccountToServerBlocklist,
25   addServerToServerBlocklist,
26   removeAccountFromServerBlocklist,
27   removeServerFromServerBlocklist
28 } from '../../../../shared/extra-utils/users/blocklist'
29
30 const expect = chai.expect
31
32 describe('Test video abuses', function () {
33   let servers: ServerInfo[] = []
34   let abuseServer2: VideoAbuse
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: VideoAbuse = 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: VideoAbuse = 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(VideoAbuseState.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: VideoAbuse = 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(VideoAbuseState.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(VideoAbuseState.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: VideoAbuseState.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(VideoAbuseState.REJECTED)
171   })
172
173   it('Should add a moderation comment', async function () {
174     const body = { state: VideoAbuseState.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(VideoAbuseState.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: VideoAbuse = 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 VideoAbuse[]) {
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 delete the video abuse', async function () {
295     this.timeout(10000)
296
297     await deleteVideoAbuse(servers[1].url, servers[1].accessToken, abuseServer2.video.uuid, abuseServer2.id)
298
299     await waitJobs(servers)
300
301     {
302       const res = await getVideoAbusesList({ url: servers[1].url, token: servers[1].accessToken })
303       expect(res.body.total).to.equal(1)
304       expect(res.body.data.length).to.equal(1)
305       expect(res.body.data[0].id).to.not.equal(abuseServer2.id)
306     }
307
308     {
309       const res = await getVideoAbusesList({ url: servers[0].url, token: servers[0].accessToken })
310       expect(res.body.total).to.equal(5)
311     }
312   })
313
314   it('Should list and filter video abuses', async function () {
315     async function list (query: Omit<Parameters<typeof getVideoAbusesList>[0], 'url' | 'token'>) {
316       const options = {
317         url: servers[0].url,
318         token: servers[0].accessToken
319       }
320
321       Object.assign(options, query)
322
323       const res = await getVideoAbusesList(options)
324
325       return res.body.data as VideoAbuse[]
326     }
327
328     expect(await list({ id: 56 })).to.have.lengthOf(0)
329     expect(await list({ id: 1 })).to.have.lengthOf(1)
330
331     expect(await list({ search: 'my super name for server 1' })).to.have.lengthOf(3)
332     expect(await list({ search: 'aaaaaaaaaaaaaaaaaaaaaaaaaa' })).to.have.lengthOf(0)
333
334     expect(await list({ searchVideo: 'my second super name for server 1' })).to.have.lengthOf(1)
335
336     expect(await list({ searchVideoChannel: 'root' })).to.have.lengthOf(3)
337     expect(await list({ searchVideoChannel: 'aaaa' })).to.have.lengthOf(0)
338
339     expect(await list({ searchReporter: 'user2' })).to.have.lengthOf(1)
340     expect(await list({ searchReporter: 'root' })).to.have.lengthOf(4)
341
342     expect(await list({ searchReportee: 'root' })).to.have.lengthOf(3)
343     expect(await list({ searchReportee: 'aaaa' })).to.have.lengthOf(0)
344
345     expect(await list({ videoIs: 'deleted' })).to.have.lengthOf(1)
346     expect(await list({ videoIs: 'blacklisted' })).to.have.lengthOf(0)
347
348     expect(await list({ state: VideoAbuseState.ACCEPTED })).to.have.lengthOf(0)
349     expect(await list({ state: VideoAbuseState.PENDING })).to.have.lengthOf(5)
350   })
351
352   after(async function () {
353     await cleanupTests(servers)
354   })
355 })