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