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