Merge branch 'master' into develop
[oweals/peertube.git] / server / tests / api / server / handle-down.ts
1 /* tslint:disable:no-unused-expression */
2
3 import * as chai from 'chai'
4 import 'mocha'
5 import { JobState, Video } from '../../../../shared/models'
6 import { VideoPrivacy } from '../../../../shared/models/videos'
7 import { VideoCommentThreadTree } from '../../../../shared/models/videos/video-comment.model'
8
9 import {
10   cleanupTests,
11   completeVideoCheck,
12   flushAndRunMultipleServers,
13   getVideo,
14   getVideosList,
15   immutableAssign,
16   killallServers,
17   reRunServer,
18   ServerInfo,
19   setAccessTokensToServers,
20   unfollow,
21   updateVideo,
22   uploadVideo,
23   wait
24 } from '../../../../shared/extra-utils'
25 import { follow, getFollowersListPaginationAndSort } from '../../../../shared/extra-utils/server/follows'
26 import { getJobsListPaginationAndSort, waitJobs } from '../../../../shared/extra-utils/server/jobs'
27 import {
28   addVideoCommentReply,
29   addVideoCommentThread,
30   getVideoCommentThreads,
31   getVideoThreadComments
32 } from '../../../../shared/extra-utils/videos/video-comments'
33
34 const expect = chai.expect
35
36 describe('Test handle downs', function () {
37   let servers: ServerInfo[] = []
38   let threadIdServer1: number
39   let threadIdServer2: number
40   let commentIdServer1: number
41   let commentIdServer2: number
42   let missedVideo1: Video
43   let missedVideo2: Video
44   let unlistedVideo: Video
45
46   const videoAttributes = {
47     name: 'my super name for server 1',
48     category: 5,
49     licence: 4,
50     language: 'ja',
51     nsfw: true,
52     privacy: VideoPrivacy.PUBLIC,
53     description: 'my super description for server 1',
54     support: 'my super support text for server 1',
55     tags: [ 'tag1p1', 'tag2p1' ],
56     fixture: 'video_short1.webm'
57   }
58
59   const unlistedVideoAttributes = immutableAssign(videoAttributes, {
60     privacy: VideoPrivacy.UNLISTED
61   })
62
63   const checkAttributes = {
64     name: 'my super name for server 1',
65     category: 5,
66     licence: 4,
67     language: 'ja',
68     nsfw: true,
69     description: 'my super description for server 1',
70     support: 'my super support text for server 1',
71     account: {
72       name: 'root',
73       host: 'localhost:9001'
74     },
75     isLocal: false,
76     duration: 10,
77     tags: [ 'tag1p1', 'tag2p1' ],
78     privacy: VideoPrivacy.PUBLIC,
79     commentsEnabled: true,
80     downloadEnabled: true,
81     channel: {
82       name: 'root_channel',
83       displayName: 'Main root channel',
84       description: '',
85       isLocal: false
86     },
87     fixture: 'video_short1.webm',
88     files: [
89       {
90         resolution: 720,
91         size: 572456
92       }
93     ]
94   }
95
96   const unlistedCheckAttributes = immutableAssign(checkAttributes, {
97     privacy: VideoPrivacy.UNLISTED
98   })
99
100   before(async function () {
101     this.timeout(30000)
102
103     servers = await flushAndRunMultipleServers(3)
104
105     // Get the access tokens
106     await setAccessTokensToServers(servers)
107   })
108
109   it('Should remove followers that are often down', async function () {
110     this.timeout(60000)
111
112     // Server 2 and 3 follow server 1
113     await follow(servers[1].url, [ servers[0].url ], servers[1].accessToken)
114     await follow(servers[2].url, [ servers[0].url ], servers[2].accessToken)
115
116     await waitJobs(servers)
117
118     // Upload a video to server 1
119     await uploadVideo(servers[0].url, servers[0].accessToken, videoAttributes)
120
121     await waitJobs(servers)
122
123     // And check all servers have this video
124     for (const server of servers) {
125       const res = await getVideosList(server.url)
126       expect(res.body.data).to.be.an('array')
127       expect(res.body.data).to.have.lengthOf(1)
128     }
129
130     // Kill server 2
131     killallServers([ servers[1] ])
132
133     // Remove server 2 follower
134     for (let i = 0; i < 10; i++) {
135       await uploadVideo(servers[ 0 ].url, servers[ 0 ].accessToken, videoAttributes)
136     }
137
138     await waitJobs(servers[0])
139
140     // Kill server 3
141     killallServers([ servers[2] ])
142
143     const resLastVideo1 = await uploadVideo(servers[ 0 ].url, servers[ 0 ].accessToken, videoAttributes)
144     missedVideo1 = resLastVideo1.body.video
145
146     const resLastVideo2 = await uploadVideo(servers[ 0 ].url, servers[ 0 ].accessToken, videoAttributes)
147     missedVideo2 = resLastVideo2.body.video
148
149     // Unlisted video
150     let resVideo = await uploadVideo(servers[ 0 ].url, servers[ 0 ].accessToken, unlistedVideoAttributes)
151     unlistedVideo = resVideo.body.video
152
153     // Add comments to video 2
154     {
155       const text = 'thread 1'
156       let resComment = await addVideoCommentThread(servers[0].url, servers[0].accessToken, missedVideo2.uuid, text)
157       let comment = resComment.body.comment
158       threadIdServer1 = comment.id
159
160       resComment = await addVideoCommentReply(servers[0].url, servers[0].accessToken, missedVideo2.uuid, comment.id, 'comment 1-1')
161       comment = resComment.body.comment
162
163       resComment = await addVideoCommentReply(servers[0].url, servers[0].accessToken, missedVideo2.uuid, comment.id, 'comment 1-2')
164       commentIdServer1 = resComment.body.comment.id
165     }
166
167     await waitJobs(servers[0])
168     // Wait scheduler
169     await wait(11000)
170
171     // Only server 3 is still a follower of server 1
172     const res = await getFollowersListPaginationAndSort(servers[0].url, 0, 2, 'createdAt')
173     expect(res.body.data).to.be.an('array')
174     expect(res.body.data).to.have.lengthOf(1)
175     expect(res.body.data[0].follower.host).to.equal('localhost:9003')
176   })
177
178   it('Should not have pending/processing jobs anymore', async function () {
179     const states: JobState[] = [ 'waiting', 'active' ]
180
181     for (const state of states) {
182       const res = await getJobsListPaginationAndSort(servers[ 0 ].url, servers[ 0 ].accessToken, state,0, 50, '-createdAt')
183       expect(res.body.data).to.have.length(0)
184     }
185   })
186
187   it('Should re-follow server 1', async function () {
188     this.timeout(35000)
189
190     await reRunServer(servers[1])
191     await reRunServer(servers[2])
192
193     await unfollow(servers[1].url, servers[1].accessToken, servers[0])
194     await waitJobs(servers)
195
196     await follow(servers[1].url, [ servers[0].url ], servers[1].accessToken)
197
198     await waitJobs(servers)
199
200     const res = await getFollowersListPaginationAndSort(servers[0].url, 0, 2, 'createdAt')
201     expect(res.body.data).to.be.an('array')
202     expect(res.body.data).to.have.lengthOf(2)
203   })
204
205   it('Should send an update to server 3, and automatically fetch the video', async function () {
206     this.timeout(15000)
207
208     const res1 = await getVideosList(servers[2].url)
209     expect(res1.body.data).to.be.an('array')
210     expect(res1.body.data).to.have.lengthOf(11)
211
212     await updateVideo(servers[0].url, servers[0].accessToken, missedVideo1.uuid, { })
213     await updateVideo(servers[0].url, servers[0].accessToken, unlistedVideo.uuid, { })
214
215     await waitJobs(servers)
216
217     const res = await getVideosList(servers[2].url)
218     expect(res.body.data).to.be.an('array')
219     // 1 video is unlisted
220     expect(res.body.data).to.have.lengthOf(12)
221
222     // Check unlisted video
223     const resVideo = await getVideo(servers[2].url, unlistedVideo.uuid)
224     expect(resVideo.body).not.to.be.undefined
225
226     await completeVideoCheck(servers[2].url, resVideo.body, unlistedCheckAttributes)
227   })
228
229   it('Should send comments on a video to server 3, and automatically fetch the video', async function () {
230     this.timeout(25000)
231
232     await addVideoCommentReply(servers[0].url, servers[0].accessToken, missedVideo2.uuid, commentIdServer1, 'comment 1-3')
233
234     await waitJobs(servers)
235
236     const resVideo = await getVideo(servers[2].url, missedVideo2.uuid)
237     expect(resVideo.body).not.to.be.undefined
238
239     {
240       let resComment = await getVideoCommentThreads(servers[2].url, missedVideo2.uuid, 0, 5)
241       expect(resComment.body.data).to.be.an('array')
242       expect(resComment.body.data).to.have.lengthOf(1)
243
244       threadIdServer2 = resComment.body.data[0].id
245
246       resComment = await getVideoThreadComments(servers[2].url, missedVideo2.uuid, threadIdServer2)
247
248       const tree: VideoCommentThreadTree = resComment.body
249       expect(tree.comment.text).equal('thread 1')
250       expect(tree.children).to.have.lengthOf(1)
251
252       const firstChild = tree.children[0]
253       expect(firstChild.comment.text).to.equal('comment 1-1')
254       expect(firstChild.children).to.have.lengthOf(1)
255
256       const childOfFirstChild = firstChild.children[0]
257       expect(childOfFirstChild.comment.text).to.equal('comment 1-2')
258       expect(childOfFirstChild.children).to.have.lengthOf(1)
259
260       const childOfChildFirstChild = childOfFirstChild.children[0]
261       expect(childOfChildFirstChild.comment.text).to.equal('comment 1-3')
262       expect(childOfChildFirstChild.children).to.have.lengthOf(0)
263
264       commentIdServer2 = childOfChildFirstChild.comment.id
265     }
266   })
267
268   it('Should correctly reply to the comment', async function () {
269     this.timeout(15000)
270
271     await addVideoCommentReply(servers[2].url, servers[2].accessToken, missedVideo2.uuid, commentIdServer2, 'comment 1-4')
272
273     await waitJobs(servers)
274
275     {
276       const resComment = await getVideoThreadComments(servers[0].url, missedVideo2.uuid, threadIdServer1)
277
278       const tree: VideoCommentThreadTree = resComment.body
279       expect(tree.comment.text).equal('thread 1')
280       expect(tree.children).to.have.lengthOf(1)
281
282       const firstChild = tree.children[0]
283       expect(firstChild.comment.text).to.equal('comment 1-1')
284       expect(firstChild.children).to.have.lengthOf(1)
285
286       const childOfFirstChild = firstChild.children[0]
287       expect(childOfFirstChild.comment.text).to.equal('comment 1-2')
288       expect(childOfFirstChild.children).to.have.lengthOf(1)
289
290       const childOfChildFirstChild = childOfFirstChild.children[0]
291       expect(childOfChildFirstChild.comment.text).to.equal('comment 1-3')
292       expect(childOfChildFirstChild.children).to.have.lengthOf(1)
293
294       const childOfChildOfChildOfFirstChild = childOfChildFirstChild.children[0]
295       expect(childOfChildOfChildOfFirstChild.comment.text).to.equal('comment 1-4')
296       expect(childOfChildOfChildOfFirstChild.children).to.have.lengthOf(0)
297     }
298   })
299
300   after(async function () {
301     await cleanupTests(servers)
302   })
303 })