1 /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
3 import * as chai from 'chai'
4 import { completeVideoCheck } from '@server/tests/shared'
5 import { wait } from '@shared/core-utils'
6 import { HttpStatusCode, JobState, VideoCreateResult, VideoPrivacy } from '@shared/models'
10 createMultipleServers,
13 setAccessTokensToServers,
15 } from '@shared/server-commands'
17 const expect = chai.expect
19 describe('Test handle downs', function () {
20 let servers: PeerTubeServer[] = []
21 let threadIdServer1: number
22 let threadIdServer2: number
23 let commentIdServer1: number
24 let commentIdServer2: number
25 let missedVideo1: VideoCreateResult
26 let missedVideo2: VideoCreateResult
27 let unlistedVideo: VideoCreateResult
29 const videoIdsServer1: string[] = []
31 const videoAttributes = {
32 name: 'my super name for server 1',
37 privacy: VideoPrivacy.PUBLIC,
38 description: 'my super description for server 1',
39 support: 'my super support text for server 1',
40 tags: [ 'tag1p1', 'tag2p1' ],
41 fixture: 'video_short1.webm'
44 const unlistedVideoAttributes = { ...videoAttributes, privacy: VideoPrivacy.UNLISTED }
46 let checkAttributes: any
47 let unlistedCheckAttributes: any
49 let commentCommands: CommentsCommand[]
51 before(async function () {
54 servers = await createMultipleServers(3)
55 commentCommands = servers.map(s => s.comments)
58 name: 'my super name for server 1',
63 description: 'my super description for server 1',
64 support: 'my super support text for server 1',
67 host: 'localhost:' + servers[0].port
71 tags: [ 'tag1p1', 'tag2p1' ],
72 privacy: VideoPrivacy.PUBLIC,
73 commentsEnabled: true,
74 downloadEnabled: true,
77 displayName: 'Main root channel',
81 fixture: 'video_short1.webm',
89 unlistedCheckAttributes = { ...checkAttributes, privacy: VideoPrivacy.UNLISTED }
91 // Get the access tokens
92 await setAccessTokensToServers(servers)
95 it('Should remove followers that are often down', async function () {
98 // Server 2 and 3 follow server 1
99 await servers[1].follows.follow({ hosts: [ servers[0].url ] })
100 await servers[2].follows.follow({ hosts: [ servers[0].url ] })
102 await waitJobs(servers)
104 // Upload a video to server 1
105 await servers[0].videos.upload({ attributes: videoAttributes })
107 await waitJobs(servers)
109 // And check all servers have this video
110 for (const server of servers) {
111 const { data } = await server.videos.list()
112 expect(data).to.be.an('array')
113 expect(data).to.have.lengthOf(1)
117 await killallServers([ servers[1] ])
119 // Remove server 2 follower
120 for (let i = 0; i < 10; i++) {
121 await servers[0].videos.upload({ attributes: videoAttributes })
124 await waitJobs([ servers[0], servers[2] ])
127 await killallServers([ servers[2] ])
129 missedVideo1 = await servers[0].videos.upload({ attributes: videoAttributes })
131 missedVideo2 = await servers[0].videos.upload({ attributes: videoAttributes })
134 unlistedVideo = await servers[0].videos.upload({ attributes: unlistedVideoAttributes })
136 // Add comments to video 2
138 const text = 'thread 1'
139 let comment = await commentCommands[0].createThread({ videoId: missedVideo2.uuid, text })
140 threadIdServer1 = comment.id
142 comment = await commentCommands[0].addReply({ videoId: missedVideo2.uuid, toCommentId: comment.id, text: 'comment 1-1' })
144 const created = await commentCommands[0].addReply({ videoId: missedVideo2.uuid, toCommentId: comment.id, text: 'comment 1-2' })
145 commentIdServer1 = created.id
148 await waitJobs(servers[0])
152 // Only server 3 is still a follower of server 1
153 const body = await servers[0].follows.getFollowers({ start: 0, count: 2, sort: 'createdAt' })
154 expect(body.data).to.be.an('array')
155 expect(body.data).to.have.lengthOf(1)
156 expect(body.data[0].follower.host).to.equal('localhost:' + servers[2].port)
159 it('Should not have pending/processing jobs anymore', async function () {
160 const states: JobState[] = [ 'waiting', 'active' ]
162 for (const state of states) {
163 const body = await servers[0].jobs.list({
169 expect(body.data).to.have.length(0)
173 it('Should re-follow server 1', async function () {
176 await servers[1].run()
177 await servers[2].run()
179 await servers[1].follows.unfollow({ target: servers[0] })
180 await waitJobs(servers)
182 await servers[1].follows.follow({ hosts: [ servers[0].url ] })
184 await waitJobs(servers)
186 const body = await servers[0].follows.getFollowers({ start: 0, count: 2, sort: 'createdAt' })
187 expect(body.data).to.be.an('array')
188 expect(body.data).to.have.lengthOf(2)
191 it('Should send an update to server 3, and automatically fetch the video', async function () {
195 const { data } = await servers[2].videos.list()
196 expect(data).to.be.an('array')
197 expect(data).to.have.lengthOf(11)
200 await servers[0].videos.update({ id: missedVideo1.uuid })
201 await servers[0].videos.update({ id: unlistedVideo.uuid })
203 await waitJobs(servers)
206 const { data } = await servers[2].videos.list()
207 expect(data).to.be.an('array')
208 // 1 video is unlisted
209 expect(data).to.have.lengthOf(12)
212 // Check unlisted video
213 const video = await servers[2].videos.get({ id: unlistedVideo.uuid })
214 await completeVideoCheck(servers[2], video, unlistedCheckAttributes)
217 it('Should send comments on a video to server 3, and automatically fetch the video', async function () {
220 await commentCommands[0].addReply({ videoId: missedVideo2.uuid, toCommentId: commentIdServer1, text: 'comment 1-3' })
222 await waitJobs(servers)
224 await servers[2].videos.get({ id: missedVideo2.uuid })
227 const { data } = await servers[2].comments.listThreads({ videoId: missedVideo2.uuid })
228 expect(data).to.be.an('array')
229 expect(data).to.have.lengthOf(1)
231 threadIdServer2 = data[0].id
233 const tree = await servers[2].comments.getThread({ videoId: missedVideo2.uuid, threadId: threadIdServer2 })
234 expect(tree.comment.text).equal('thread 1')
235 expect(tree.children).to.have.lengthOf(1)
237 const firstChild = tree.children[0]
238 expect(firstChild.comment.text).to.equal('comment 1-1')
239 expect(firstChild.children).to.have.lengthOf(1)
241 const childOfFirstChild = firstChild.children[0]
242 expect(childOfFirstChild.comment.text).to.equal('comment 1-2')
243 expect(childOfFirstChild.children).to.have.lengthOf(1)
245 const childOfChildFirstChild = childOfFirstChild.children[0]
246 expect(childOfChildFirstChild.comment.text).to.equal('comment 1-3')
247 expect(childOfChildFirstChild.children).to.have.lengthOf(0)
249 commentIdServer2 = childOfChildFirstChild.comment.id
253 it('Should correctly reply to the comment', async function () {
256 await servers[2].comments.addReply({ videoId: missedVideo2.uuid, toCommentId: commentIdServer2, text: 'comment 1-4' })
258 await waitJobs(servers)
260 const tree = await commentCommands[0].getThread({ videoId: missedVideo2.uuid, threadId: threadIdServer1 })
262 expect(tree.comment.text).equal('thread 1')
263 expect(tree.children).to.have.lengthOf(1)
265 const firstChild = tree.children[0]
266 expect(firstChild.comment.text).to.equal('comment 1-1')
267 expect(firstChild.children).to.have.lengthOf(1)
269 const childOfFirstChild = firstChild.children[0]
270 expect(childOfFirstChild.comment.text).to.equal('comment 1-2')
271 expect(childOfFirstChild.children).to.have.lengthOf(1)
273 const childOfChildFirstChild = childOfFirstChild.children[0]
274 expect(childOfChildFirstChild.comment.text).to.equal('comment 1-3')
275 expect(childOfChildFirstChild.children).to.have.lengthOf(1)
277 const childOfChildOfChildOfFirstChild = childOfChildFirstChild.children[0]
278 expect(childOfChildOfChildOfFirstChild.comment.text).to.equal('comment 1-4')
279 expect(childOfChildOfChildOfFirstChild.children).to.have.lengthOf(0)
282 it('Should upload many videos on server 1', async function () {
285 for (let i = 0; i < 10; i++) {
286 const uuid = (await servers[0].videos.quickUpload({ name: 'video ' + i })).uuid
287 videoIdsServer1.push(uuid)
290 await waitJobs(servers)
292 for (const id of videoIdsServer1) {
293 await servers[1].videos.get({ id })
296 await waitJobs(servers)
297 await servers[1].sql.setActorFollowScores(20)
299 // Wait video expiration
302 // Refresh video -> score + 10 = 30
303 await servers[1].videos.get({ id: videoIdsServer1[0] })
305 await waitJobs(servers)
308 it('Should remove followings that are down', async function () {
311 await killallServers([ servers[0] ])
313 // Wait video expiration
316 for (let i = 0; i < 5; i++) {
318 await servers[1].videos.get({ id: videoIdsServer1[i] })
319 await waitJobs([ servers[1] ])
324 for (const id of videoIdsServer1) {
325 await servers[1].videos.get({ id, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
329 after(async function () {
330 await cleanupTests(servers)