1 /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
4 import * as chai from 'chai'
5 import { expectStartWith } from '@server/tests/shared'
6 import { ActorFollow, FollowState } from '@shared/models'
12 setAccessTokensToServers,
14 } from '@shared/server-commands'
16 const expect = chai.expect
18 async function checkServer1And2HasFollowers (servers: PeerTubeServer[], state = 'accepted') {
20 servers[0].follows.getFollowings.bind(servers[0].follows),
21 servers[1].follows.getFollowers.bind(servers[1].follows)
24 for (const fn of fns) {
25 const body = await fn({ start: 0, count: 5, sort: 'createdAt' })
26 expect(body.total).to.equal(1)
28 const follow = body.data[0]
29 expect(follow.state).to.equal(state)
30 expect(follow.follower.url).to.equal(servers[0].url + '/accounts/peertube')
31 expect(follow.following.url).to.equal(servers[1].url + '/accounts/peertube')
35 async function checkFollows (options: {
36 follower: PeerTubeServer
37 followerState: FollowState | 'deleted'
39 following: PeerTubeServer
40 followingState: FollowState | 'deleted'
42 const { follower, followerState, followingState, following } = options
44 const followerUrl = follower.url + '/accounts/peertube'
45 const followingUrl = following.url + '/accounts/peertube'
46 const finder = (d: ActorFollow) => d.follower.url === followerUrl && d.following.url === followingUrl
49 const { data } = await follower.follows.getFollowings()
50 const follow = data.find(finder)
52 if (followerState === 'deleted') {
53 expect(follow).to.not.exist
55 expect(follow.state).to.equal(followerState)
56 expect(follow.follower.url).to.equal(followerUrl)
57 expect(follow.following.url).to.equal(followingUrl)
62 const { data } = await following.follows.getFollowers()
63 const follow = data.find(finder)
65 if (followingState === 'deleted') {
66 expect(follow).to.not.exist
68 expect(follow.state).to.equal(followingState)
69 expect(follow.follower.url).to.equal(followerUrl)
70 expect(follow.following.url).to.equal(followingUrl)
75 async function checkNoFollowers (servers: PeerTubeServer[]) {
77 servers[0].follows.getFollowings.bind(servers[0].follows),
78 servers[1].follows.getFollowers.bind(servers[1].follows)
81 for (const fn of fns) {
82 const body = await fn({ start: 0, count: 5, sort: 'createdAt', state: 'accepted' })
83 expect(body.total).to.equal(0)
87 describe('Test follows moderation', function () {
88 let servers: PeerTubeServer[] = []
89 let commands: FollowsCommand[]
91 before(async function () {
94 servers = await createMultipleServers(3)
96 // Get the access tokens
97 await setAccessTokensToServers(servers)
99 commands = servers.map(s => s.follows)
102 it('Should have server 1 following server 2', async function () {
105 await commands[0].follow({ hosts: [ servers[1].url ] })
107 await waitJobs(servers)
110 it('Should have correct follows', async function () {
111 await checkServer1And2HasFollowers(servers)
114 it('Should remove follower on server 2', async function () {
117 await commands[1].removeFollower({ follower: servers[0] })
119 await waitJobs(servers)
122 it('Should not not have follows anymore', async function () {
123 await checkNoFollowers(servers)
126 it('Should disable followers on server 2', async function () {
133 manualApproval: false
138 await servers[1].config.updateCustomSubConfig({ newConfig: subConfig })
140 await commands[0].follow({ hosts: [ servers[1].url ] })
141 await waitJobs(servers)
143 await checkNoFollowers(servers)
146 it('Should re enable followers on server 2', async function () {
153 manualApproval: false
158 await servers[1].config.updateCustomSubConfig({ newConfig: subConfig })
160 await commands[0].follow({ hosts: [ servers[1].url ] })
161 await waitJobs(servers)
163 await checkServer1And2HasFollowers(servers)
166 it('Should manually approve followers', async function () {
169 await commands[0].unfollow({ target: servers[1] })
170 await waitJobs(servers)
181 await servers[1].config.updateCustomSubConfig({ newConfig: subConfig })
182 await servers[2].config.updateCustomSubConfig({ newConfig: subConfig })
184 await commands[0].follow({ hosts: [ servers[1].url ] })
185 await waitJobs(servers)
187 await checkServer1And2HasFollowers(servers, 'pending')
190 it('Should accept a follower', async function () {
193 await commands[1].acceptFollower({ follower: 'peertube@' + servers[0].host })
194 await waitJobs(servers)
196 await checkServer1And2HasFollowers(servers)
199 it('Should reject another follower', async function () {
202 await commands[0].follow({ hosts: [ servers[2].url ] })
203 await waitJobs(servers)
206 const body = await commands[0].getFollowings()
207 expect(body.total).to.equal(2)
211 const body = await commands[1].getFollowers()
212 expect(body.total).to.equal(1)
216 const body = await commands[2].getFollowers()
217 expect(body.total).to.equal(1)
220 await commands[2].rejectFollower({ follower: 'peertube@' + servers[0].host })
221 await waitJobs(servers)
225 const { data } = await commands[0].getFollowings({ state: 'accepted' })
226 expect(data).to.have.lengthOf(1)
230 const { data } = await commands[0].getFollowings({ state: 'rejected' })
231 expect(data).to.have.lengthOf(1)
232 expectStartWith(data[0].following.url, servers[2].url)
238 const { data } = await commands[2].getFollowers({ state: 'accepted' })
239 expect(data).to.have.lengthOf(0)
243 const { data } = await commands[2].getFollowers({ state: 'rejected' })
244 expect(data).to.have.lengthOf(1)
245 expectStartWith(data[0].follower.url, servers[0].url)
250 it('Should not change the follow on refollow with and without auto accept', async function () {
251 const run = async () => {
252 await commands[0].follow({ hosts: [ servers[2].url ] })
253 await waitJobs(servers)
256 follower: servers[0],
257 followerState: 'rejected',
258 following: servers[2],
259 followingState: 'rejected'
263 await servers[2].config.updateExistingSubConfig({ newConfig: { followers: { instance: { manualApproval: false } } } })
266 await servers[2].config.updateExistingSubConfig({ newConfig: { followers: { instance: { manualApproval: true } } } })
270 it('Should not change the rejected status on unfollow', async function () {
271 await commands[0].unfollow({ target: servers[2] })
272 await waitJobs(servers)
275 follower: servers[0],
276 followerState: 'deleted',
277 following: servers[2],
278 followingState: 'rejected'
282 it('Should delete the follower and add again the follower', async function () {
283 await commands[2].removeFollower({ follower: servers[0] })
284 await waitJobs(servers)
286 await commands[0].follow({ hosts: [ servers[2].url ] })
287 await waitJobs(servers)
290 follower: servers[0],
291 followerState: 'pending',
292 following: servers[2],
293 followingState: 'pending'
297 it('Should be able to reject a previously accepted follower', async function () {
298 await commands[1].rejectFollower({ follower: 'peertube@' + servers[0].host })
299 await waitJobs(servers)
302 follower: servers[0],
303 followerState: 'rejected',
304 following: servers[1],
305 followingState: 'rejected'
309 it('Should be able to re accept a previously rejected follower', async function () {
310 await commands[1].acceptFollower({ follower: 'peertube@' + servers[0].host })
311 await waitJobs(servers)
314 follower: servers[0],
315 followerState: 'accepted',
316 following: servers[1],
317 followingState: 'accepted'
321 it('Should ignore follow requests of muted servers', async function () {
322 await servers[1].blocklist.addToServerBlocklist({ server: servers[0].host })
324 await commands[0].unfollow({ target: servers[1] })
326 await waitJobs(servers)
329 follower: servers[0],
330 followerState: 'deleted',
331 following: servers[1],
332 followingState: 'deleted'
335 await commands[0].follow({ hosts: [ servers[1].host ] })
336 await waitJobs(servers)
339 follower: servers[0],
340 followerState: 'rejected',
341 following: servers[1],
342 followingState: 'deleted'
346 after(async function () {
347 await cleanupTests(servers)