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