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