Move job queue to redis
[oweals/peertube.git] / server / tests / api / videos / multiple-servers.ts
1 /* tslint:disable:no-unused-expression */
2
3 import * as chai from 'chai'
4 import 'mocha'
5 import { join } from 'path'
6 import * as request from 'supertest'
7 import { VideoPrivacy } from '../../../../shared/models/videos'
8 import { VideoComment, VideoCommentThreadTree } from '../../../../shared/models/videos/video-comment.model'
9
10 import {
11   addVideoChannel, checkVideoFilesWereRemoved, completeVideoCheck, createUser, dateIsValid, doubleFollow, flushAndRunMultipleServers,
12   flushTests, getVideo,
13   getVideoChannelsList, getVideosList, killallServers, rateVideo, removeVideo, ServerInfo, setAccessTokensToServers, testImage,
14   updateVideo, uploadVideo, userLogin, viewVideo, wait, webtorrentAdd
15 } from '../../utils'
16 import {
17   addVideoCommentReply, addVideoCommentThread, deleteVideoComment, getVideoCommentThreads,
18   getVideoThreadComments
19 } from '../../utils/videos/video-comments'
20
21 const expect = chai.expect
22
23 describe('Test multiple servers', function () {
24   let servers: ServerInfo[] = []
25   const toRemove = []
26   let videoUUID = ''
27   let videoChannelId: number
28
29   before(async function () {
30     this.timeout(120000)
31
32     servers = await flushAndRunMultipleServers(3)
33
34     // Get the access tokens
35     await setAccessTokensToServers(servers)
36
37     const videoChannel = {
38       name: 'my channel',
39       description: 'super channel'
40     }
41     await addVideoChannel(servers[0].url, servers[0].accessToken, videoChannel)
42     const channelRes = await getVideoChannelsList(servers[0].url, 0, 1)
43     videoChannelId = channelRes.body.data[0].id
44
45     // Server 1 and server 2 follow each other
46     await doubleFollow(servers[0], servers[1])
47     // Server 1 and server 3 follow each other
48     await doubleFollow(servers[0], servers[2])
49     // Server 2 and server 3 follow each other
50     await doubleFollow(servers[1], servers[2])
51   })
52
53   it('Should not have videos for all servers', async function () {
54     for (const server of servers) {
55       const res = await getVideosList(server.url)
56       const videos = res.body.data
57       expect(videos).to.be.an('array')
58       expect(videos.length).to.equal(0)
59     }
60   })
61
62   describe('Should upload the video and propagate on each server', function () {
63     it('Should upload the video on server 1 and propagate on each server', async function () {
64       this.timeout(25000)
65
66       const videoAttributes = {
67         name: 'my super name for server 1',
68         category: 5,
69         licence: 4,
70         language: 9,
71         nsfw: true,
72         description: 'my super description for server 1',
73         tags: [ 'tag1p1', 'tag2p1' ],
74         channelId: videoChannelId,
75         fixture: 'video_short1.webm'
76       }
77       await uploadVideo(servers[0].url, servers[0].accessToken, videoAttributes)
78
79       await wait(10000)
80
81       // All servers should have this video
82       for (const server of servers) {
83         const isLocal = server.url === 'http://localhost:9001'
84         const checkAttributes = {
85           name: 'my super name for server 1',
86           category: 5,
87           licence: 4,
88           language: 9,
89           nsfw: true,
90           description: 'my super description for server 1',
91           host: 'localhost:9001',
92           account: 'root',
93           isLocal,
94           duration: 10,
95           tags: [ 'tag1p1', 'tag2p1' ],
96           privacy: VideoPrivacy.PUBLIC,
97           commentsEnabled: true,
98           channel: {
99             name: 'my channel',
100             description: 'super channel',
101             isLocal
102           },
103           fixture: 'video_short1.webm',
104           files: [
105             {
106               resolution: 720,
107               size: 572456
108             }
109           ]
110         }
111
112         const res = await getVideosList(server.url)
113         const videos = res.body.data
114         expect(videos).to.be.an('array')
115         expect(videos.length).to.equal(1)
116         const video = videos[0]
117
118         await completeVideoCheck(server.url, video, checkAttributes)
119       }
120     })
121
122     it('Should upload the video on server 2 and propagate on each server', async function () {
123       this.timeout(50000)
124
125       const user = {
126         username: 'user1',
127         password: 'super_password'
128       }
129       await createUser(servers[1].url, servers[1].accessToken, user.username, user.password)
130       const userAccessToken = await userLogin(servers[1], user)
131
132       const videoAttributes = {
133         name: 'my super name for server 2',
134         category: 4,
135         licence: 3,
136         language: 11,
137         nsfw: true,
138         description: 'my super description for server 2',
139         tags: [ 'tag1p2', 'tag2p2', 'tag3p2' ],
140         fixture: 'video_short2.webm'
141       }
142       await uploadVideo(servers[1].url, userAccessToken, videoAttributes)
143
144       // Transcoding
145       await wait(30000)
146
147       // All servers should have this video
148       for (const server of servers) {
149         const isLocal = server.url === 'http://localhost:9002'
150         const checkAttributes = {
151           name: 'my super name for server 2',
152           category: 4,
153           licence: 3,
154           language: 11,
155           nsfw: true,
156           description: 'my super description for server 2',
157           host: 'localhost:9002',
158           account: 'user1',
159           isLocal,
160           commentsEnabled: true,
161           duration: 5,
162           tags: [ 'tag1p2', 'tag2p2', 'tag3p2' ],
163           privacy: VideoPrivacy.PUBLIC,
164           channel: {
165             name: 'Default user1 channel',
166             description: 'super channel',
167             isLocal
168           },
169           fixture: 'video_short2.webm',
170           files: [
171             {
172               resolution: 240,
173               size: 190000
174             },
175             {
176               resolution: 360,
177               size: 280000
178             },
179             {
180               resolution: 480,
181               size: 390000
182             },
183             {
184               resolution: 720,
185               size: 710000
186             }
187           ]
188         }
189
190         const res = await getVideosList(server.url)
191         const videos = res.body.data
192         expect(videos).to.be.an('array')
193         expect(videos.length).to.equal(2)
194         const video = videos[1]
195
196         await completeVideoCheck(server.url, video, checkAttributes)
197       }
198     })
199
200     it('Should upload two videos on server 3 and propagate on each server', async function () {
201       this.timeout(45000)
202
203       const videoAttributes1 = {
204         name: 'my super name for server 3',
205         category: 6,
206         licence: 5,
207         language: 11,
208         nsfw: true,
209         description: 'my super description for server 3',
210         tags: [ 'tag1p3' ],
211         fixture: 'video_short3.webm'
212       }
213       await uploadVideo(servers[2].url, servers[2].accessToken, videoAttributes1)
214
215       const videoAttributes2 = {
216         name: 'my super name for server 3-2',
217         category: 7,
218         licence: 6,
219         language: 12,
220         nsfw: false,
221         description: 'my super description for server 3-2',
222         tags: [ 'tag2p3', 'tag3p3', 'tag4p3' ],
223         fixture: 'video_short.webm'
224       }
225       await uploadVideo(servers[2].url, servers[2].accessToken, videoAttributes2)
226
227       await wait(10000)
228
229       // All servers should have this video
230       for (const server of servers) {
231         const isLocal = server.url === 'http://localhost:9003'
232         const res = await getVideosList(server.url)
233
234         const videos = res.body.data
235         expect(videos).to.be.an('array')
236         expect(videos.length).to.equal(4)
237
238         // We not sure about the order of the two last uploads
239         let video1 = null
240         let video2 = null
241         if (videos[2].name === 'my super name for server 3') {
242           video1 = videos[2]
243           video2 = videos[3]
244         } else {
245           video1 = videos[3]
246           video2 = videos[2]
247         }
248
249         const checkAttributesVideo1 = {
250           name: 'my super name for server 3',
251           category: 6,
252           licence: 5,
253           language: 11,
254           nsfw: true,
255           description: 'my super description for server 3',
256           host: 'localhost:9003',
257           account: 'root',
258           isLocal,
259           duration: 5,
260           commentsEnabled: true,
261           tags: [ 'tag1p3' ],
262           privacy: VideoPrivacy.PUBLIC,
263           channel: {
264             name: 'Default root channel',
265             description: '',
266             isLocal
267           },
268           fixture: 'video_short3.webm',
269           files: [
270             {
271               resolution: 720,
272               size: 292677
273             }
274           ]
275         }
276         await completeVideoCheck(server.url, video1, checkAttributesVideo1)
277
278         const checkAttributesVideo2 = {
279           name: 'my super name for server 3-2',
280           category: 7,
281           licence: 6,
282           language: 12,
283           nsfw: false,
284           description: 'my super description for server 3-2',
285           host: 'localhost:9003',
286           account: 'root',
287           commentsEnabled: true,
288           isLocal,
289           duration: 5,
290           tags: [ 'tag2p3', 'tag3p3', 'tag4p3' ],
291           privacy: VideoPrivacy.PUBLIC,
292           channel: {
293             name: 'Default root channel',
294             description: '',
295             isLocal
296           },
297           fixture: 'video_short.webm',
298           files: [
299             {
300               resolution: 720,
301               size: 218910
302             }
303           ]
304         }
305         await completeVideoCheck(server.url, video2, checkAttributesVideo2)
306       }
307     })
308   })
309
310   describe('Should seed the uploaded video', function () {
311     it('Should add the file 1 by asking server 3', async function () {
312       this.timeout(10000)
313
314       const res = await getVideosList(servers[2].url)
315
316       const video = res.body.data[0]
317       toRemove.push(res.body.data[2])
318       toRemove.push(res.body.data[3])
319
320       const res2 = await getVideo(servers[2].url, video.id)
321       const videoDetails = res2.body
322
323       const torrent = await webtorrentAdd(videoDetails.files[0].magnetUri, true)
324       expect(torrent.files).to.be.an('array')
325       expect(torrent.files.length).to.equal(1)
326       expect(torrent.files[0].path).to.exist.and.to.not.equal('')
327     })
328
329     it('Should add the file 2 by asking server 1', async function () {
330       this.timeout(10000)
331
332       const res = await getVideosList(servers[0].url)
333
334       const video = res.body.data[1]
335       const res2 = await getVideo(servers[0].url, video.id)
336       const videoDetails = res2.body
337
338       const torrent = await webtorrentAdd(videoDetails.files[0].magnetUri, true)
339       expect(torrent.files).to.be.an('array')
340       expect(torrent.files.length).to.equal(1)
341       expect(torrent.files[0].path).to.exist.and.to.not.equal('')
342     })
343
344     it('Should add the file 3 by asking server 2', async function () {
345       this.timeout(10000)
346
347       const res = await getVideosList(servers[1].url)
348
349       const video = res.body.data[2]
350       const res2 = await getVideo(servers[1].url, video.id)
351       const videoDetails = res2.body
352
353       const torrent = await webtorrentAdd(videoDetails.files[0].magnetUri, true)
354       expect(torrent.files).to.be.an('array')
355       expect(torrent.files.length).to.equal(1)
356       expect(torrent.files[0].path).to.exist.and.to.not.equal('')
357     })
358
359     it('Should add the file 3-2 by asking server 1', async function () {
360       this.timeout(10000)
361
362       const res = await getVideosList(servers[0].url)
363
364       const video = res.body.data[3]
365       const res2 = await getVideo(servers[0].url, video.id)
366       const videoDetails = res2.body
367
368       const torrent = await webtorrentAdd(videoDetails.files[0].magnetUri)
369       expect(torrent.files).to.be.an('array')
370       expect(torrent.files.length).to.equal(1)
371       expect(torrent.files[0].path).to.exist.and.to.not.equal('')
372     })
373
374     it('Should add the file 2 in 360p by asking server 1', async function () {
375       this.timeout(10000)
376
377       const res = await getVideosList(servers[0].url)
378
379       const video = res.body.data.find(v => v.name === 'my super name for server 2')
380       const res2 = await getVideo(servers[0].url, video.id)
381       const videoDetails = res2.body
382
383       const file = videoDetails.files.find(f => f.resolution === 360)
384       expect(file).not.to.be.undefined
385
386       const torrent = await webtorrentAdd(file.magnetUri)
387       expect(torrent.files).to.be.an('array')
388       expect(torrent.files.length).to.equal(1)
389       expect(torrent.files[0].path).to.exist.and.to.not.equal('')
390     })
391   })
392
393   describe('Should update video views, likes and dislikes', function () {
394     let localVideosServer3 = []
395     let remoteVideosServer1 = []
396     let remoteVideosServer2 = []
397     let remoteVideosServer3 = []
398
399     before(async function () {
400       const res1 = await getVideosList(servers[0].url)
401       remoteVideosServer1 = res1.body.data.filter(video => video.isLocal === false).map(video => video.uuid)
402
403       const res2 = await getVideosList(servers[1].url)
404       remoteVideosServer2 = res2.body.data.filter(video => video.isLocal === false).map(video => video.uuid)
405
406       const res3 = await getVideosList(servers[2].url)
407       localVideosServer3 = res3.body.data.filter(video => video.isLocal === true).map(video => video.uuid)
408       remoteVideosServer3 = res3.body.data.filter(video => video.isLocal === false).map(video => video.uuid)
409     })
410
411     it('Should view multiple videos on owned servers', async function () {
412       this.timeout(10000)
413
414       const tasks: Promise<any>[] = []
415       tasks.push(viewVideo(servers[2].url, localVideosServer3[0]))
416       tasks.push(viewVideo(servers[2].url, localVideosServer3[0]))
417       tasks.push(viewVideo(servers[2].url, localVideosServer3[0]))
418       tasks.push(viewVideo(servers[2].url, localVideosServer3[1]))
419
420       await Promise.all(tasks)
421
422       await wait(5000)
423
424       for (const server of servers) {
425         const res = await getVideosList(server.url)
426
427         const videos = res.body.data
428         const video0 = videos.find(v => v.uuid === localVideosServer3[0])
429         const video1 = videos.find(v => v.uuid === localVideosServer3[1])
430
431         expect(video0.views).to.equal(3)
432         expect(video1.views).to.equal(1)
433       }
434     })
435
436     it('Should view multiple videos on each servers', async function () {
437       this.timeout(15000)
438
439       const tasks: Promise<any>[] = []
440       tasks.push(viewVideo(servers[0].url, remoteVideosServer1[0]))
441       tasks.push(viewVideo(servers[1].url, remoteVideosServer2[0]))
442       tasks.push(viewVideo(servers[1].url, remoteVideosServer2[0]))
443       tasks.push(viewVideo(servers[2].url, remoteVideosServer3[0]))
444       tasks.push(viewVideo(servers[2].url, remoteVideosServer3[1]))
445       tasks.push(viewVideo(servers[2].url, remoteVideosServer3[1]))
446       tasks.push(viewVideo(servers[2].url, remoteVideosServer3[1]))
447       tasks.push(viewVideo(servers[2].url, localVideosServer3[1]))
448       tasks.push(viewVideo(servers[2].url, localVideosServer3[1]))
449       tasks.push(viewVideo(servers[2].url, localVideosServer3[1]))
450
451       await Promise.all(tasks)
452
453       await wait(10000)
454
455       let baseVideos = null
456
457       for (const server of servers) {
458         const res = await getVideosList(server.url)
459
460         const videos = res.body.data
461
462         // Initialize base videos for future comparisons
463         if (baseVideos === null) {
464           baseVideos = videos
465           continue
466         }
467
468         for (const baseVideo of baseVideos) {
469           const sameVideo = videos.find(video => video.name === baseVideo.name)
470           expect(baseVideo.views).to.equal(sameVideo.views)
471         }
472       }
473     })
474
475     it('Should like and dislikes videos on different services', async function () {
476       this.timeout(20000)
477
478       await rateVideo(servers[0].url, servers[0].accessToken, remoteVideosServer1[0], 'like')
479       await wait(200)
480       await rateVideo(servers[0].url, servers[0].accessToken, remoteVideosServer1[0], 'dislike')
481       await wait(200)
482       await rateVideo(servers[0].url, servers[0].accessToken, remoteVideosServer1[0], 'like')
483       await rateVideo(servers[2].url, servers[2].accessToken, localVideosServer3[1], 'like')
484       await wait(200)
485       await rateVideo(servers[2].url, servers[2].accessToken, localVideosServer3[1], 'dislike')
486       await rateVideo(servers[2].url, servers[2].accessToken, remoteVideosServer3[1], 'dislike')
487       await wait(200)
488       await rateVideo(servers[2].url, servers[2].accessToken, remoteVideosServer3[0], 'like')
489
490       await wait(10000)
491
492       let baseVideos = null
493       for (const server of servers) {
494         const res = await getVideosList(server.url)
495
496         const videos = res.body.data
497
498         // Initialize base videos for future comparisons
499         if (baseVideos === null) {
500           baseVideos = videos
501           continue
502         }
503
504         for (const baseVideo of baseVideos) {
505           const sameVideo = videos.find(video => video.name === baseVideo.name)
506           expect(baseVideo.likes).to.equal(sameVideo.likes)
507           expect(baseVideo.dislikes).to.equal(sameVideo.dislikes)
508         }
509       }
510     })
511   })
512
513   describe('Should manipulate these videos', function () {
514     it('Should update the video 3 by asking server 3', async function () {
515       this.timeout(10000)
516
517       const attributes = {
518         name: 'my super video updated',
519         category: 10,
520         licence: 7,
521         language: 13,
522         nsfw: true,
523         description: 'my super description updated',
524         tags: [ 'tag_up_1', 'tag_up_2' ]
525       }
526
527       await updateVideo(servers[2].url, servers[2].accessToken, toRemove[0].id, attributes)
528
529       await wait(5000)
530     })
531
532     it('Should have the video 3 updated on each server', async function () {
533       this.timeout(10000)
534
535       for (const server of servers) {
536         const res = await getVideosList(server.url)
537
538         const videos = res.body.data
539         const videoUpdated = videos.find(video => video.name === 'my super video updated')
540         expect(!!videoUpdated).to.be.true
541
542         const isLocal = server.url === 'http://localhost:9003'
543         const checkAttributes = {
544           name: 'my super video updated',
545           category: 10,
546           licence: 7,
547           language: 13,
548           nsfw: true,
549           description: 'my super description updated',
550           host: 'localhost:9003',
551           account: 'root',
552           isLocal,
553           duration: 5,
554           commentsEnabled: true,
555           tags: [ 'tag_up_1', 'tag_up_2' ],
556           privacy: VideoPrivacy.PUBLIC,
557           channel: {
558             name: 'Default root channel',
559             description: '',
560             isLocal
561           },
562           fixture: 'video_short3.webm',
563           files: [
564             {
565               resolution: 720,
566               size: 292677
567             }
568           ]
569         }
570         await completeVideoCheck(server.url, videoUpdated, checkAttributes)
571       }
572     })
573
574     it('Should remove the videos 3 and 3-2 by asking server 3', async function () {
575       this.timeout(10000)
576
577       await removeVideo(servers[2].url, servers[2].accessToken, toRemove[0].id)
578       await removeVideo(servers[2].url, servers[2].accessToken, toRemove[1].id)
579
580       await wait(5000)
581     })
582
583     it('Should not have files of videos 3 and 3-2 on each server', async function () {
584       for (const server of servers) {
585         await checkVideoFilesWereRemoved(toRemove[0].uuid, server.serverNumber)
586         await checkVideoFilesWereRemoved(toRemove[1].uuid, server.serverNumber)
587       }
588     })
589
590     it('Should have videos 1 and 3 on each server', async function () {
591       for (const server of servers) {
592         const res = await getVideosList(server.url)
593
594         const videos = res.body.data
595         expect(videos).to.be.an('array')
596         expect(videos.length).to.equal(2)
597         expect(videos[0].name).not.to.equal(videos[1].name)
598         expect(videos[0].name).not.to.equal(toRemove[0].name)
599         expect(videos[1].name).not.to.equal(toRemove[0].name)
600         expect(videos[0].name).not.to.equal(toRemove[1].name)
601         expect(videos[1].name).not.to.equal(toRemove[1].name)
602
603         videoUUID = videos.find(video => video.name === 'my super name for server 1').uuid
604       }
605     })
606
607     it('Should get the same video by UUID on each server', async function () {
608       let baseVideo = null
609       for (const server of servers) {
610         const res = await getVideo(server.url, videoUUID)
611
612         const video = res.body
613
614         if (baseVideo === null) {
615           baseVideo = video
616           continue
617         }
618
619         expect(baseVideo.name).to.equal(video.name)
620         expect(baseVideo.uuid).to.equal(video.uuid)
621         expect(baseVideo.category).to.equal(video.category)
622         expect(baseVideo.language).to.equal(video.language)
623         expect(baseVideo.licence).to.equal(video.licence)
624         expect(baseVideo.category).to.equal(video.category)
625         expect(baseVideo.nsfw).to.equal(video.nsfw)
626         expect(baseVideo.accountName).to.equal(video.accountName)
627         expect(baseVideo.tags).to.deep.equal(video.tags)
628       }
629     })
630
631     it('Should get the preview from each server', async function () {
632       for (const server of servers) {
633         const res = await getVideo(server.url, videoUUID)
634         const video = res.body
635
636         const test = await testImage(server.url, 'video_short1-preview.webm', video.previewPath)
637         expect(test).to.equal(true)
638       }
639     })
640   })
641
642   describe('Should comment these videos', function () {
643     it('Should add comment (threads and replies)', async function () {
644       this.timeout(25000)
645
646       {
647         const text = 'my super first comment'
648         await addVideoCommentThread(servers[ 0 ].url, servers[ 0 ].accessToken, videoUUID, text)
649       }
650
651       {
652         const text = 'my super second comment'
653         await addVideoCommentThread(servers[ 2 ].url, servers[ 2 ].accessToken, videoUUID, text)
654       }
655
656       await wait(5000)
657
658       {
659         const res = await getVideoCommentThreads(servers[1].url, videoUUID, 0, 5)
660         const threadId = res.body.data.find(c => c.text === 'my super first comment').id
661
662         const text = 'my super answer to thread 1'
663         await addVideoCommentReply(servers[ 1 ].url, servers[ 1 ].accessToken, videoUUID, threadId, text)
664       }
665
666       await wait(5000)
667
668       {
669         const res1 = await getVideoCommentThreads(servers[2].url, videoUUID, 0, 5)
670         const threadId = res1.body.data.find(c => c.text === 'my super first comment').id
671
672         const res2 = await getVideoThreadComments(servers[2].url, videoUUID, threadId)
673         const childCommentId = res2.body.children[0].comment.id
674
675         const text3 = 'my second answer to thread 1'
676         await addVideoCommentReply(servers[ 2 ].url, servers[ 2 ].accessToken, videoUUID, threadId, text3)
677
678         const text2 = 'my super answer to answer of thread 1'
679         await addVideoCommentReply(servers[ 2 ].url, servers[ 2 ].accessToken, videoUUID, childCommentId, text2)
680       }
681
682       await wait(5000)
683     })
684
685     it('Should have these threads', async function () {
686       for (const server of servers) {
687         const res = await getVideoCommentThreads(server.url, videoUUID, 0, 5)
688
689         expect(res.body.total).to.equal(2)
690         expect(res.body.data).to.be.an('array')
691         expect(res.body.data).to.have.lengthOf(2)
692
693         {
694           const comment: VideoComment = res.body.data.find(c => c.text === 'my super first comment')
695           expect(comment).to.not.be.undefined
696           expect(comment.inReplyToCommentId).to.be.null
697           expect(comment.account.name).to.equal('root')
698           expect(comment.account.host).to.equal('localhost:9001')
699           expect(comment.totalReplies).to.equal(3)
700           expect(dateIsValid(comment.createdAt as string)).to.be.true
701           expect(dateIsValid(comment.updatedAt as string)).to.be.true
702         }
703
704         {
705           const comment: VideoComment = res.body.data.find(c => c.text === 'my super second comment')
706           expect(comment).to.not.be.undefined
707           expect(comment.inReplyToCommentId).to.be.null
708           expect(comment.account.name).to.equal('root')
709           expect(comment.account.host).to.equal('localhost:9003')
710           expect(comment.totalReplies).to.equal(0)
711           expect(dateIsValid(comment.createdAt as string)).to.be.true
712           expect(dateIsValid(comment.updatedAt as string)).to.be.true
713         }
714       }
715     })
716
717     it('Should have these comments', async function () {
718       for (const server of servers) {
719         const res1 = await getVideoCommentThreads(server.url, videoUUID, 0, 5)
720         const threadId = res1.body.data.find(c => c.text === 'my super first comment').id
721
722         const res2 = await getVideoThreadComments(server.url, videoUUID, threadId)
723
724         const tree: VideoCommentThreadTree = res2.body
725         expect(tree.comment.text).equal('my super first comment')
726         expect(tree.comment.account.name).equal('root')
727         expect(tree.comment.account.host).equal('localhost:9001')
728         expect(tree.children).to.have.lengthOf(2)
729
730         const firstChild = tree.children[0]
731         expect(firstChild.comment.text).to.equal('my super answer to thread 1')
732         expect(firstChild.comment.account.name).equal('root')
733         expect(firstChild.comment.account.host).equal('localhost:9002')
734         expect(firstChild.children).to.have.lengthOf(1)
735
736         const childOfFirstChild = firstChild.children[0]
737         expect(childOfFirstChild.comment.text).to.equal('my super answer to answer of thread 1')
738         expect(childOfFirstChild.comment.account.name).equal('root')
739         expect(childOfFirstChild.comment.account.host).equal('localhost:9003')
740         expect(childOfFirstChild.children).to.have.lengthOf(0)
741
742         const secondChild = tree.children[1]
743         expect(secondChild.comment.text).to.equal('my second answer to thread 1')
744         expect(secondChild.comment.account.name).equal('root')
745         expect(secondChild.comment.account.host).equal('localhost:9003')
746         expect(secondChild.children).to.have.lengthOf(0)
747       }
748     })
749
750     it('Should delete the thread comments', async function () {
751       this.timeout(10000)
752
753       const res1 = await getVideoCommentThreads(servers[0].url, videoUUID, 0, 5)
754       const threadId = res1.body.data.find(c => c.text === 'my super first comment').id
755       await deleteVideoComment(servers[0].url, servers[0].accessToken, videoUUID, threadId)
756
757       await wait(5000)
758     })
759
760     it('Should have the thread comments deleted on other servers too', async function () {
761       for (const server of servers) {
762         const res = await getVideoCommentThreads(server.url, videoUUID, 0, 5)
763
764         expect(res.body.total).to.equal(1)
765         expect(res.body.data).to.be.an('array')
766         expect(res.body.data).to.have.lengthOf(1)
767
768         {
769           const comment: VideoComment = res.body.data[0]
770           expect(comment).to.not.be.undefined
771           expect(comment.inReplyToCommentId).to.be.null
772           expect(comment.account.name).to.equal('root')
773           expect(comment.account.host).to.equal('localhost:9003')
774           expect(comment.totalReplies).to.equal(0)
775           expect(dateIsValid(comment.createdAt as string)).to.be.true
776           expect(dateIsValid(comment.updatedAt as string)).to.be.true
777         }
778       }
779     })
780
781     it('Should disable comments', async function () {
782       this.timeout(20000)
783
784       const attributes = {
785         commentsEnabled: false
786       }
787
788       await updateVideo(servers[0].url, servers[0].accessToken, videoUUID, attributes)
789
790       await wait(5000)
791
792       for (const server of servers) {
793         const res = await getVideo(server.url, videoUUID)
794         expect(res.body.commentsEnabled).to.be.false
795
796         const text = 'my super forbidden comment'
797         await addVideoCommentThread(server.url, server.accessToken, videoUUID, text, 409)
798       }
799     })
800   })
801
802   describe('With minimum parameters', function () {
803     it('Should upload and propagate the video', async function () {
804       this.timeout(50000)
805
806       const path = '/api/v1/videos/upload'
807
808       const req = request(servers[1].url)
809         .post(path)
810         .set('Accept', 'application/json')
811         .set('Authorization', 'Bearer ' + servers[1].accessToken)
812         .field('name', 'minimum parameters')
813         .field('privacy', '1')
814         .field('nsfw', 'false')
815         .field('channelId', '1')
816         .field('commentsEnabled', 'true')
817
818       const filePath = join(__dirname, '..', '..', 'api', 'fixtures', 'video_short.webm')
819
820       await req.attach('videofile', filePath)
821         .expect(200)
822
823       await wait(25000)
824
825       for (const server of servers) {
826         const res = await getVideosList(server.url)
827         const video = res.body.data.find(v => v.name === 'minimum parameters')
828
829         const isLocal = server.url === 'http://localhost:9002'
830         const checkAttributes = {
831           name: 'minimum parameters',
832           category: null,
833           licence: null,
834           language: null,
835           nsfw: false,
836           description: null,
837           host: 'localhost:9002',
838           account: 'root',
839           isLocal,
840           duration: 5,
841           commentsEnabled: true,
842           tags: [ ],
843           privacy: VideoPrivacy.PUBLIC,
844           channel: {
845             name: 'Default root channel',
846             description: '',
847             isLocal
848           },
849           fixture: 'video_short.webm',
850           files: [
851             {
852               resolution: 720,
853               size: 40315
854             },
855             {
856               resolution: 480,
857               size: 22808
858             },
859             {
860               resolution: 360,
861               size: 18617
862             },
863             {
864               resolution: 240,
865               size: 15217
866             }
867           ]
868         }
869         await completeVideoCheck(server.url, video, checkAttributes)
870       }
871     })
872   })
873
874   after(async function () {
875     killallServers(servers)
876
877     // Keep the logs if the test failed
878     if (this['ok']) {
879       await flushTests()
880     }
881   })
882 })