1 /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
4 import * as chai from 'chai'
5 import { HttpStatusCode } from '@shared/core-utils'
10 flushAndRunMultipleServers,
16 setAccessTokensToServers,
22 } from '@shared/extra-utils'
23 import { JobState, Video, VideoPrivacy } from '@shared/models'
25 const expect = chai.expect
27 describe('Test handle downs', function () {
28 let servers: ServerInfo[] = []
29 let threadIdServer1: number
30 let threadIdServer2: number
31 let commentIdServer1: number
32 let commentIdServer2: number
33 let missedVideo1: Video
34 let missedVideo2: Video
35 let unlistedVideo: Video
37 const videoIdsServer1: string[] = []
39 const videoAttributes = {
40 name: 'my super name for server 1',
45 privacy: VideoPrivacy.PUBLIC,
46 description: 'my super description for server 1',
47 support: 'my super support text for server 1',
48 tags: [ 'tag1p1', 'tag2p1' ],
49 fixture: 'video_short1.webm'
52 const unlistedVideoAttributes = { ...videoAttributes, privacy: VideoPrivacy.UNLISTED }
54 let checkAttributes: any
55 let unlistedCheckAttributes: any
57 let commentCommands: CommentsCommand[]
59 before(async function () {
62 servers = await flushAndRunMultipleServers(3)
63 commentCommands = servers.map(s => s.commentsCommand)
66 name: 'my super name for server 1',
71 description: 'my super description for server 1',
72 support: 'my super support text for server 1',
75 host: 'localhost:' + servers[0].port
79 tags: [ 'tag1p1', 'tag2p1' ],
80 privacy: VideoPrivacy.PUBLIC,
81 commentsEnabled: true,
82 downloadEnabled: true,
85 displayName: 'Main root channel',
89 fixture: 'video_short1.webm',
97 unlistedCheckAttributes = { ...checkAttributes, privacy: VideoPrivacy.UNLISTED }
99 // Get the access tokens
100 await setAccessTokensToServers(servers)
103 it('Should remove followers that are often down', async function () {
106 // Server 2 and 3 follow server 1
107 await servers[1].followsCommand.follow({ targets: [ servers[0].url ] })
108 await servers[2].followsCommand.follow({ targets: [ servers[0].url ] })
110 await waitJobs(servers)
112 // Upload a video to server 1
113 await uploadVideo(servers[0].url, servers[0].accessToken, videoAttributes)
115 await waitJobs(servers)
117 // And check all servers have this video
118 for (const server of servers) {
119 const res = await getVideosList(server.url)
120 expect(res.body.data).to.be.an('array')
121 expect(res.body.data).to.have.lengthOf(1)
125 await killallServers([ servers[1] ])
127 // Remove server 2 follower
128 for (let i = 0; i < 10; i++) {
129 await uploadVideo(servers[0].url, servers[0].accessToken, videoAttributes)
132 await waitJobs([ servers[0], servers[2] ])
135 await killallServers([ servers[2] ])
137 const resLastVideo1 = await uploadVideo(servers[0].url, servers[0].accessToken, videoAttributes)
138 missedVideo1 = resLastVideo1.body.video
140 const resLastVideo2 = await uploadVideo(servers[0].url, servers[0].accessToken, videoAttributes)
141 missedVideo2 = resLastVideo2.body.video
144 const resVideo = await uploadVideo(servers[0].url, servers[0].accessToken, unlistedVideoAttributes)
145 unlistedVideo = resVideo.body.video
147 // Add comments to video 2
149 const text = 'thread 1'
150 let comment = await commentCommands[0].createThread({ videoId: missedVideo2.uuid, text })
151 threadIdServer1 = comment.id
153 comment = await commentCommands[0].addReply({ videoId: missedVideo2.uuid, toCommentId: comment.id, text: 'comment 1-1' })
155 const created = await commentCommands[0].addReply({ videoId: missedVideo2.uuid, toCommentId: comment.id, text: 'comment 1-2' })
156 commentIdServer1 = created.id
159 await waitJobs(servers[0])
163 // Only server 3 is still a follower of server 1
164 const body = await servers[0].followsCommand.getFollowers({ start: 0, count: 2, sort: 'createdAt' })
165 expect(body.data).to.be.an('array')
166 expect(body.data).to.have.lengthOf(1)
167 expect(body.data[0].follower.host).to.equal('localhost:' + servers[2].port)
170 it('Should not have pending/processing jobs anymore', async function () {
171 const states: JobState[] = [ 'waiting', 'active' ]
173 for (const state of states) {
174 const body = await servers[0].jobsCommand.getJobsList({
180 expect(body.data).to.have.length(0)
184 it('Should re-follow server 1', async function () {
187 await reRunServer(servers[1])
188 await reRunServer(servers[2])
190 await servers[1].followsCommand.unfollow({ target: servers[0] })
191 await waitJobs(servers)
193 await servers[1].followsCommand.follow({ targets: [ servers[0].url ] })
195 await waitJobs(servers)
197 const body = await servers[0].followsCommand.getFollowers({ start: 0, count: 2, sort: 'createdAt' })
198 expect(body.data).to.be.an('array')
199 expect(body.data).to.have.lengthOf(2)
202 it('Should send an update to server 3, and automatically fetch the video', async function () {
205 const res1 = await getVideosList(servers[2].url)
206 expect(res1.body.data).to.be.an('array')
207 expect(res1.body.data).to.have.lengthOf(11)
209 await updateVideo(servers[0].url, servers[0].accessToken, missedVideo1.uuid, {})
210 await updateVideo(servers[0].url, servers[0].accessToken, unlistedVideo.uuid, {})
212 await waitJobs(servers)
214 const res = await getVideosList(servers[2].url)
215 expect(res.body.data).to.be.an('array')
216 // 1 video is unlisted
217 expect(res.body.data).to.have.lengthOf(12)
219 // Check unlisted video
220 const resVideo = await getVideo(servers[2].url, unlistedVideo.uuid)
221 expect(resVideo.body).not.to.be.undefined
223 await completeVideoCheck(servers[2].url, resVideo.body, unlistedCheckAttributes)
226 it('Should send comments on a video to server 3, and automatically fetch the video', async function () {
229 await commentCommands[0].addReply({ videoId: missedVideo2.uuid, toCommentId: commentIdServer1, text: 'comment 1-3' })
231 await waitJobs(servers)
233 const resVideo = await getVideo(servers[2].url, missedVideo2.uuid)
234 expect(resVideo.body).not.to.be.undefined
237 const { data } = await servers[2].commentsCommand.listThreads({ videoId: missedVideo2.uuid })
238 expect(data).to.be.an('array')
239 expect(data).to.have.lengthOf(1)
241 threadIdServer2 = data[0].id
243 const tree = await servers[2].commentsCommand.getThread({ videoId: missedVideo2.uuid, threadId: threadIdServer2 })
244 expect(tree.comment.text).equal('thread 1')
245 expect(tree.children).to.have.lengthOf(1)
247 const firstChild = tree.children[0]
248 expect(firstChild.comment.text).to.equal('comment 1-1')
249 expect(firstChild.children).to.have.lengthOf(1)
251 const childOfFirstChild = firstChild.children[0]
252 expect(childOfFirstChild.comment.text).to.equal('comment 1-2')
253 expect(childOfFirstChild.children).to.have.lengthOf(1)
255 const childOfChildFirstChild = childOfFirstChild.children[0]
256 expect(childOfChildFirstChild.comment.text).to.equal('comment 1-3')
257 expect(childOfChildFirstChild.children).to.have.lengthOf(0)
259 commentIdServer2 = childOfChildFirstChild.comment.id
263 it('Should correctly reply to the comment', async function () {
266 await servers[2].commentsCommand.addReply({ videoId: missedVideo2.uuid, toCommentId: commentIdServer2, text: 'comment 1-4' })
268 await waitJobs(servers)
270 const tree = await commentCommands[0].getThread({ videoId: missedVideo2.uuid, threadId: threadIdServer1 })
272 expect(tree.comment.text).equal('thread 1')
273 expect(tree.children).to.have.lengthOf(1)
275 const firstChild = tree.children[0]
276 expect(firstChild.comment.text).to.equal('comment 1-1')
277 expect(firstChild.children).to.have.lengthOf(1)
279 const childOfFirstChild = firstChild.children[0]
280 expect(childOfFirstChild.comment.text).to.equal('comment 1-2')
281 expect(childOfFirstChild.children).to.have.lengthOf(1)
283 const childOfChildFirstChild = childOfFirstChild.children[0]
284 expect(childOfChildFirstChild.comment.text).to.equal('comment 1-3')
285 expect(childOfChildFirstChild.children).to.have.lengthOf(1)
287 const childOfChildOfChildOfFirstChild = childOfChildFirstChild.children[0]
288 expect(childOfChildOfChildOfFirstChild.comment.text).to.equal('comment 1-4')
289 expect(childOfChildOfChildOfFirstChild.children).to.have.lengthOf(0)
292 it('Should upload many videos on server 1', async function () {
295 for (let i = 0; i < 10; i++) {
296 const uuid = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video ' + i })).uuid
297 videoIdsServer1.push(uuid)
300 await waitJobs(servers)
302 for (const id of videoIdsServer1) {
303 await getVideo(servers[1].url, id)
306 await waitJobs(servers)
307 await servers[1].sqlCommand.setActorFollowScores(20)
309 // Wait video expiration
312 // Refresh video -> score + 10 = 30
313 await getVideo(servers[1].url, videoIdsServer1[0])
315 await waitJobs(servers)
318 it('Should remove followings that are down', async function () {
321 await killallServers([ servers[0] ])
323 // Wait video expiration
326 for (let i = 0; i < 5; i++) {
328 await getVideo(servers[1].url, videoIdsServer1[i])
329 await waitJobs([ servers[1] ])
334 for (const id of videoIdsServer1) {
335 await getVideo(servers[1].url, id, HttpStatusCode.FORBIDDEN_403)
339 after(async function () {
340 await cleanupTests(servers)