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