Filter on follows actor types in about page
[oweals/peertube.git] / server / tests / api / server / follows.ts
1 /* tslint:disable:no-unused-expression */
2
3 import * as chai from 'chai'
4 import 'mocha'
5 import { Video, VideoPrivacy } from '../../../../shared/models/videos'
6 import { VideoComment, VideoCommentThreadTree } from '../../../../shared/models/videos/video-comment.model'
7 import { cleanupTests, completeVideoCheck } from '../../../../shared/extra-utils'
8 import {
9   flushAndRunMultipleServers,
10   getVideosList,
11   ServerInfo,
12   setAccessTokensToServers,
13   uploadVideo
14 } from '../../../../shared/extra-utils/index'
15 import { dateIsValid } from '../../../../shared/extra-utils/miscs/miscs'
16 import {
17   follow,
18   getFollowersListPaginationAndSort,
19   getFollowingListPaginationAndSort,
20   unfollow
21 } from '../../../../shared/extra-utils/server/follows'
22 import { expectAccountFollows } from '../../../../shared/extra-utils/users/accounts'
23 import { userLogin } from '../../../../shared/extra-utils/users/login'
24 import { createUser } from '../../../../shared/extra-utils/users/users'
25 import {
26   addVideoCommentReply,
27   addVideoCommentThread,
28   getVideoCommentThreads,
29   getVideoThreadComments
30 } from '../../../../shared/extra-utils/videos/video-comments'
31 import { rateVideo } from '../../../../shared/extra-utils/videos/videos'
32 import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
33 import { createVideoCaption, listVideoCaptions, testCaptionFile } from '../../../../shared/extra-utils/videos/video-captions'
34 import { VideoCaption } from '../../../../shared/models/videos/caption/video-caption.model'
35
36 const expect = chai.expect
37
38 describe('Test follows', function () {
39   let servers: ServerInfo[] = []
40
41   before(async function () {
42     this.timeout(30000)
43
44     servers = await flushAndRunMultipleServers(3)
45
46     // Get the access tokens
47     await setAccessTokensToServers(servers)
48   })
49
50   it('Should not have followers', async function () {
51     for (const server of servers) {
52       const res = await getFollowersListPaginationAndSort({ url: server.url, start: 0, count: 5, sort: 'createdAt' })
53       const follows = res.body.data
54
55       expect(res.body.total).to.equal(0)
56       expect(follows).to.be.an('array')
57       expect(follows.length).to.equal(0)
58     }
59   })
60
61   it('Should not have following', async function () {
62     for (const server of servers) {
63       const res = await getFollowingListPaginationAndSort({ url: server.url, start: 0, count: 5, sort: 'createdAt' })
64       const follows = res.body.data
65
66       expect(res.body.total).to.equal(0)
67       expect(follows).to.be.an('array')
68       expect(follows.length).to.equal(0)
69     }
70   })
71
72   it('Should have server 1 following server 2 and 3', async function () {
73     this.timeout(30000)
74
75     await follow(servers[0].url, [ servers[1].url, servers[2].url ], servers[0].accessToken)
76
77     await waitJobs(servers)
78   })
79
80   it('Should have 2 followings on server 1', async function () {
81     let res = await getFollowingListPaginationAndSort({ url: servers[ 0 ].url, start: 0, count: 1, sort: 'createdAt' })
82     let follows = res.body.data
83
84     expect(res.body.total).to.equal(2)
85     expect(follows).to.be.an('array')
86     expect(follows.length).to.equal(1)
87
88     res = await getFollowingListPaginationAndSort({ url: servers[ 0 ].url, start: 1, count: 1, sort: 'createdAt' })
89     follows = follows.concat(res.body.data)
90
91     const server2Follow = follows.find(f => f.following.host === 'localhost:' + servers[1].port)
92     const server3Follow = follows.find(f => f.following.host === 'localhost:' + servers[2].port)
93
94     expect(server2Follow).to.not.be.undefined
95     expect(server3Follow).to.not.be.undefined
96     expect(server2Follow.state).to.equal('accepted')
97     expect(server3Follow.state).to.equal('accepted')
98   })
99
100   it('Should search/filter followings on server 1', async function () {
101     const sort = 'createdAt'
102     const start = 0
103     const count = 1
104     const url = servers[ 0 ].url
105
106     {
107       const search = ':' + servers[1].port
108
109       {
110         const res = await getFollowingListPaginationAndSort({ url, start, count, sort, search })
111         const follows = res.body.data
112
113         expect(res.body.total).to.equal(1)
114         expect(follows.length).to.equal(1)
115         expect(follows[ 0 ].following.host).to.equal('localhost:' + servers[ 1 ].port)
116       }
117
118       {
119         const res = await getFollowingListPaginationAndSort({ url, start, count, sort, search, state: 'accepted' })
120         expect(res.body.total).to.equal(1)
121         expect(res.body.data).to.have.lengthOf(1)
122       }
123
124       {
125         const res = await getFollowingListPaginationAndSort({ url, start, count, sort, search, state: 'accepted', actorType: 'Person' })
126         expect(res.body.total).to.equal(0)
127         expect(res.body.data).to.have.lengthOf(0)
128       }
129
130       {
131         const res = await getFollowingListPaginationAndSort({
132           url,
133           start,
134           count,
135           sort,
136           search,
137           state: 'accepted',
138           actorType: 'Application'
139         })
140         expect(res.body.total).to.equal(1)
141         expect(res.body.data).to.have.lengthOf(1)
142       }
143
144       {
145         const res = await getFollowingListPaginationAndSort({ url, start, count, sort, search, state: 'pending' })
146         expect(res.body.total).to.equal(0)
147         expect(res.body.data).to.have.lengthOf(0)
148       }
149     }
150
151     {
152       const res = await getFollowingListPaginationAndSort({ url, start, count, sort, search: 'bla' })
153       const follows = res.body.data
154
155       expect(res.body.total).to.equal(0)
156       expect(follows.length).to.equal(0)
157     }
158   })
159
160   it('Should have 0 followings on server 2 and 3', async function () {
161     for (const server of [ servers[1], servers[2] ]) {
162       const res = await getFollowingListPaginationAndSort({ url: server.url, start: 0, count: 5, sort: 'createdAt' })
163       const follows = res.body.data
164
165       expect(res.body.total).to.equal(0)
166       expect(follows).to.be.an('array')
167       expect(follows.length).to.equal(0)
168     }
169   })
170
171   it('Should have 1 followers on server 2 and 3', async function () {
172     for (const server of [ servers[1], servers[2] ]) {
173       let res = await getFollowersListPaginationAndSort({ url: server.url, start: 0, count: 1, sort: 'createdAt' })
174
175       let follows = res.body.data
176       expect(res.body.total).to.equal(1)
177       expect(follows).to.be.an('array')
178       expect(follows.length).to.equal(1)
179       expect(follows[0].follower.host).to.equal('localhost:' + servers[0].port)
180     }
181   })
182
183   it('Should search/filter followers on server 2', async function () {
184     const url = servers[ 2 ].url
185     const start = 0
186     const count = 5
187     const sort = 'createdAt'
188
189     {
190       const search = servers[0].port + ''
191
192       {
193         const res = await getFollowersListPaginationAndSort({ url, start, count, sort, search })
194         const follows = res.body.data
195
196         expect(res.body.total).to.equal(1)
197         expect(follows.length).to.equal(1)
198         expect(follows[ 0 ].following.host).to.equal('localhost:' + servers[ 2 ].port)
199       }
200
201       {
202         const res = await getFollowersListPaginationAndSort({ url, start, count, sort, search, state: 'accepted' })
203         expect(res.body.total).to.equal(1)
204         expect(res.body.data).to.have.lengthOf(1)
205       }
206
207       {
208         const res = await getFollowersListPaginationAndSort({ url, start, count, sort, search, state: 'accepted', actorType: 'Person' })
209         expect(res.body.total).to.equal(0)
210         expect(res.body.data).to.have.lengthOf(0)
211       }
212
213       {
214         const res = await getFollowersListPaginationAndSort({
215           url,
216           start,
217           count,
218           sort,
219           search,
220           state: 'accepted',
221           actorType: 'Application'
222         })
223         expect(res.body.total).to.equal(1)
224         expect(res.body.data).to.have.lengthOf(1)
225       }
226
227       {
228         const res = await getFollowersListPaginationAndSort({ url, start, count, sort, search, state: 'pending' })
229         expect(res.body.total).to.equal(0)
230         expect(res.body.data).to.have.lengthOf(0)
231       }
232     }
233
234     {
235       const res = await getFollowersListPaginationAndSort({ url, start, count, sort, search: 'bla' })
236       const follows = res.body.data
237
238       expect(res.body.total).to.equal(0)
239       expect(follows.length).to.equal(0)
240     }
241   })
242
243   it('Should have 0 followers on server 1', async function () {
244     const res = await getFollowersListPaginationAndSort({ url: servers[ 0 ].url, start: 0, count: 5, sort: 'createdAt' })
245     const follows = res.body.data
246
247     expect(res.body.total).to.equal(0)
248     expect(follows).to.be.an('array')
249     expect(follows.length).to.equal(0)
250   })
251
252   it('Should have the correct follows counts', async function () {
253     await expectAccountFollows(servers[0].url, 'peertube@localhost:' + servers[0].port, 0, 2)
254     await expectAccountFollows(servers[0].url, 'peertube@localhost:' + servers[1].port, 1, 0)
255     await expectAccountFollows(servers[0].url, 'peertube@localhost:' + servers[2].port, 1, 0)
256
257     // Server 2 and 3 does not know server 1 follow another server (there was not a refresh)
258     await expectAccountFollows(servers[1].url, 'peertube@localhost:' + servers[0].port, 0, 1)
259     await expectAccountFollows(servers[1].url, 'peertube@localhost:' + servers[1].port, 1, 0)
260
261     await expectAccountFollows(servers[2].url, 'peertube@localhost:' + servers[0].port, 0, 1)
262     await expectAccountFollows(servers[2].url, 'peertube@localhost:' + servers[2].port, 1, 0)
263   })
264
265   it('Should unfollow server 3 on server 1', async function () {
266     this.timeout(5000)
267
268     await unfollow(servers[0].url, servers[0].accessToken, servers[2])
269
270     await waitJobs(servers)
271   })
272
273   it('Should not follow server 3 on server 1 anymore', async function () {
274     const res = await getFollowingListPaginationAndSort({ url: servers[ 0 ].url, start: 0, count: 2, sort: 'createdAt' })
275     let follows = res.body.data
276
277     expect(res.body.total).to.equal(1)
278     expect(follows).to.be.an('array')
279     expect(follows.length).to.equal(1)
280
281     expect(follows[0].following.host).to.equal('localhost:' + servers[1].port)
282   })
283
284   it('Should not have server 1 as follower on server 3 anymore', async function () {
285     const res = await getFollowersListPaginationAndSort({ url: servers[ 2 ].url, start: 0, count: 1, sort: 'createdAt' })
286
287     let follows = res.body.data
288     expect(res.body.total).to.equal(0)
289     expect(follows).to.be.an('array')
290     expect(follows.length).to.equal(0)
291   })
292
293   it('Should have the correct follows counts 2', async function () {
294     await expectAccountFollows(servers[0].url, 'peertube@localhost:' + servers[0].port, 0, 1)
295     await expectAccountFollows(servers[0].url, 'peertube@localhost:' + servers[1].port, 1, 0)
296
297     await expectAccountFollows(servers[1].url, 'peertube@localhost:' + servers[0].port, 0, 1)
298     await expectAccountFollows(servers[1].url, 'peertube@localhost:' + servers[1].port, 1, 0)
299
300     await expectAccountFollows(servers[2].url, 'peertube@localhost:' + servers[0].port, 0, 0)
301     await expectAccountFollows(servers[2].url, 'peertube@localhost:' + servers[2].port, 0, 0)
302   })
303
304   it('Should upload a video on server 2 and 3 and propagate only the video of server 2', async function () {
305     this.timeout(35000)
306
307     await uploadVideo(servers[1].url, servers[1].accessToken, { name: 'server2' })
308     await uploadVideo(servers[2].url, servers[2].accessToken, { name: 'server3' })
309
310     await waitJobs(servers)
311
312     let res = await getVideosList(servers[0].url)
313     expect(res.body.total).to.equal(1)
314     expect(res.body.data[0].name).to.equal('server2')
315
316     res = await getVideosList(servers[1].url)
317     expect(res.body.total).to.equal(1)
318     expect(res.body.data[0].name).to.equal('server2')
319
320     res = await getVideosList(servers[2].url)
321     expect(res.body.total).to.equal(1)
322     expect(res.body.data[0].name).to.equal('server3')
323   })
324
325   describe('Should propagate data on a new following', function () {
326     let video4: Video
327
328     before(async function () {
329       this.timeout(20000)
330
331       const video4Attributes = {
332         name: 'server3-4',
333         category: 2,
334         nsfw: true,
335         licence: 6,
336         tags: [ 'tag1', 'tag2', 'tag3' ]
337       }
338
339       await uploadVideo(servers[ 2 ].url, servers[ 2 ].accessToken, { name: 'server3-2' })
340       await uploadVideo(servers[ 2 ].url, servers[ 2 ].accessToken, { name: 'server3-3' })
341       await uploadVideo(servers[ 2 ].url, servers[ 2 ].accessToken, video4Attributes)
342       await uploadVideo(servers[ 2 ].url, servers[ 2 ].accessToken, { name: 'server3-5' })
343       await uploadVideo(servers[ 2 ].url, servers[ 2 ].accessToken, { name: 'server3-6' })
344
345       {
346         const user = { username: 'captain', password: 'password' }
347         await createUser({ url: servers[ 2 ].url, accessToken: servers[ 2 ].accessToken, username: user.username, password: user.password })
348         const userAccessToken = await userLogin(servers[ 2 ], user)
349
350         const resVideos = await getVideosList(servers[ 2 ].url)
351         video4 = resVideos.body.data.find(v => v.name === 'server3-4')
352
353         {
354           await rateVideo(servers[ 2 ].url, servers[ 2 ].accessToken, video4.id, 'like')
355           await rateVideo(servers[ 2 ].url, userAccessToken, video4.id, 'dislike')
356         }
357
358         {
359           const text = 'my super first comment'
360           const res = await addVideoCommentThread(servers[ 2 ].url, servers[ 2 ].accessToken, video4.id, text)
361           const threadId = res.body.comment.id
362
363           const text1 = 'my super answer to thread 1'
364           const childCommentRes = await addVideoCommentReply(servers[ 2 ].url, servers[ 2 ].accessToken, video4.id, threadId, text1)
365           const childCommentId = childCommentRes.body.comment.id
366
367           const text2 = 'my super answer to answer of thread 1'
368           await addVideoCommentReply(servers[ 2 ].url, servers[ 2 ].accessToken, video4.id, childCommentId, text2)
369
370           const text3 = 'my second answer to thread 1'
371           await addVideoCommentReply(servers[ 2 ].url, servers[ 2 ].accessToken, video4.id, threadId, text3)
372         }
373
374         {
375           await createVideoCaption({
376             url: servers[2].url,
377             accessToken: servers[2].accessToken,
378             language: 'ar',
379             videoId: video4.id,
380             fixture: 'subtitle-good2.vtt'
381           })
382         }
383       }
384
385       await waitJobs(servers)
386
387       // Server 1 follows server 3
388       await follow(servers[ 0 ].url, [ servers[ 2 ].url ], servers[ 0 ].accessToken)
389
390       await waitJobs(servers)
391     })
392
393     it('Should have the correct follows counts 3', async function () {
394       await expectAccountFollows(servers[0].url, 'peertube@localhost:' + servers[0].port, 0, 2)
395       await expectAccountFollows(servers[0].url, 'peertube@localhost:' + servers[1].port, 1, 0)
396       await expectAccountFollows(servers[0].url, 'peertube@localhost:' + servers[2].port, 1, 0)
397
398       await expectAccountFollows(servers[1].url, 'peertube@localhost:' + servers[0].port, 0, 1)
399       await expectAccountFollows(servers[1].url, 'peertube@localhost:' + servers[1].port, 1, 0)
400
401       await expectAccountFollows(servers[2].url, 'peertube@localhost:' + servers[0].port, 0, 2)
402       await expectAccountFollows(servers[2].url, 'peertube@localhost:' + servers[2].port, 1, 0)
403     })
404
405     it('Should have propagated videos', async function () {
406       const res = await getVideosList(servers[ 0 ].url)
407       expect(res.body.total).to.equal(7)
408
409       const video2 = res.body.data.find(v => v.name === 'server3-2')
410       video4 = res.body.data.find(v => v.name === 'server3-4')
411       const video6 = res.body.data.find(v => v.name === 'server3-6')
412
413       expect(video2).to.not.be.undefined
414       expect(video4).to.not.be.undefined
415       expect(video6).to.not.be.undefined
416
417       const isLocal = false
418       const checkAttributes = {
419         name: 'server3-4',
420         category: 2,
421         licence: 6,
422         language: 'zh',
423         nsfw: true,
424         description: 'my super description',
425         support: 'my super support text',
426         account: {
427           name: 'root',
428           host: 'localhost:' + servers[2].port
429         },
430         isLocal,
431         commentsEnabled: true,
432         downloadEnabled: true,
433         duration: 5,
434         tags: [ 'tag1', 'tag2', 'tag3' ],
435         privacy: VideoPrivacy.PUBLIC,
436         likes: 1,
437         dislikes: 1,
438         channel: {
439           displayName: 'Main root channel',
440           name: 'root_channel',
441           description: '',
442           isLocal
443         },
444         fixture: 'video_short.webm',
445         files: [
446           {
447             resolution: 720,
448             size: 218910
449           }
450         ]
451       }
452       await completeVideoCheck(servers[ 0 ].url, video4, checkAttributes)
453     })
454
455     it('Should have propagated comments', async function () {
456       const res1 = await getVideoCommentThreads(servers[0].url, video4.id, 0, 5)
457
458       expect(res1.body.total).to.equal(1)
459       expect(res1.body.data).to.be.an('array')
460       expect(res1.body.data).to.have.lengthOf(1)
461
462       const comment: VideoComment = res1.body.data[0]
463       expect(comment.inReplyToCommentId).to.be.null
464       expect(comment.text).equal('my super first comment')
465       expect(comment.videoId).to.equal(video4.id)
466       expect(comment.id).to.equal(comment.threadId)
467       expect(comment.account.name).to.equal('root')
468       expect(comment.account.host).to.equal('localhost:' + servers[2].port)
469       expect(comment.totalReplies).to.equal(3)
470       expect(dateIsValid(comment.createdAt as string)).to.be.true
471       expect(dateIsValid(comment.updatedAt as string)).to.be.true
472
473       const threadId = comment.threadId
474
475       const res2 = await getVideoThreadComments(servers[0].url, video4.id, threadId)
476
477       const tree: VideoCommentThreadTree = res2.body
478       expect(tree.comment.text).equal('my super first comment')
479       expect(tree.children).to.have.lengthOf(2)
480
481       const firstChild = tree.children[0]
482       expect(firstChild.comment.text).to.equal('my super answer to thread 1')
483       expect(firstChild.children).to.have.lengthOf(1)
484
485       const childOfFirstChild = firstChild.children[0]
486       expect(childOfFirstChild.comment.text).to.equal('my super answer to answer of thread 1')
487       expect(childOfFirstChild.children).to.have.lengthOf(0)
488
489       const secondChild = tree.children[1]
490       expect(secondChild.comment.text).to.equal('my second answer to thread 1')
491       expect(secondChild.children).to.have.lengthOf(0)
492     })
493
494     it('Should have propagated captions', async function () {
495       const res = await listVideoCaptions(servers[0].url, video4.id)
496       expect(res.body.total).to.equal(1)
497       expect(res.body.data).to.have.lengthOf(1)
498
499       const caption1: VideoCaption = res.body.data[0]
500       expect(caption1.language.id).to.equal('ar')
501       expect(caption1.language.label).to.equal('Arabic')
502       expect(caption1.captionPath).to.equal('/static/video-captions/' + video4.uuid + '-ar.vtt')
503       await testCaptionFile(servers[0].url, caption1.captionPath, 'Subtitle good 2.')
504     })
505
506     it('Should unfollow server 3 on server 1 and does not list server 3 videos', async function () {
507       this.timeout(5000)
508
509       await unfollow(servers[0].url, servers[0].accessToken, servers[2])
510
511       await waitJobs(servers)
512
513       let res = await getVideosList(servers[ 0 ].url)
514       expect(res.body.total).to.equal(1)
515     })
516
517   })
518
519   after(async function () {
520     await cleanupTests(servers)
521   })
522 })