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