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