1 /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
3 import { expect } from 'chai'
4 import { completeVideoCheck, SQLCommand } 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 describe('Test handle downs', function () {
18 let servers: PeerTubeServer[] = []
19 let sqlCommands: SQLCommand[] = []
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',
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)
94 sqlCommands = servers.map(s => new SQLCommand(s))
97 it('Should remove followers that are often down', async function () {
100 // Server 2 and 3 follow server 1
101 await servers[1].follows.follow({ hosts: [ servers[0].url ] })
102 await servers[2].follows.follow({ hosts: [ servers[0].url ] })
104 await waitJobs(servers)
106 // Upload a video to server 1
107 await servers[0].videos.upload({ attributes: videoAttributes })
109 await waitJobs(servers)
111 // And check all servers have this video
112 for (const server of servers) {
113 const { data } = await server.videos.list()
114 expect(data).to.be.an('array')
115 expect(data).to.have.lengthOf(1)
119 await killallServers([ servers[1] ])
121 // Remove server 2 follower
122 for (let i = 0; i < 10; i++) {
123 await servers[0].videos.upload({ attributes: videoAttributes })
126 await waitJobs([ servers[0], servers[2] ])
129 await killallServers([ servers[2] ])
131 missedVideo1 = await servers[0].videos.upload({ attributes: videoAttributes })
133 missedVideo2 = await servers[0].videos.upload({ attributes: videoAttributes })
136 unlistedVideo = await servers[0].videos.upload({ attributes: unlistedVideoAttributes })
138 // Add comments to video 2
140 const text = 'thread 1'
141 let comment = await commentCommands[0].createThread({ videoId: missedVideo2.uuid, text })
142 threadIdServer1 = comment.id
144 comment = await commentCommands[0].addReply({ videoId: missedVideo2.uuid, toCommentId: comment.id, text: 'comment 1-1' })
146 const created = await commentCommands[0].addReply({ videoId: missedVideo2.uuid, toCommentId: comment.id, text: 'comment 1-2' })
147 commentIdServer1 = created.id
150 await waitJobs(servers[0])
154 // Only server 3 is still a follower of server 1
155 const body = await servers[0].follows.getFollowers({ start: 0, count: 2, sort: 'createdAt' })
156 expect(body.data).to.be.an('array')
157 expect(body.data).to.have.lengthOf(1)
158 expect(body.data[0].follower.host).to.equal(servers[2].host)
161 it('Should not have pending/processing jobs anymore', async function () {
162 const states: JobState[] = [ 'waiting', 'active' ]
164 for (const state of states) {
165 const body = await servers[0].jobs.list({
171 expect(body.data).to.have.length(0)
175 it('Should re-follow server 1', async function () {
178 await servers[1].run()
179 await servers[2].run()
181 await servers[1].follows.unfollow({ target: servers[0] })
182 await waitJobs(servers)
184 await servers[1].follows.follow({ hosts: [ servers[0].url ] })
186 await waitJobs(servers)
188 const body = await servers[0].follows.getFollowers({ start: 0, count: 2, sort: 'createdAt' })
189 expect(body.data).to.be.an('array')
190 expect(body.data).to.have.lengthOf(2)
193 it('Should send an update to server 3, and automatically fetch the video', async function () {
197 const { data } = await servers[2].videos.list()
198 expect(data).to.be.an('array')
199 expect(data).to.have.lengthOf(11)
202 await servers[0].videos.update({ id: missedVideo1.uuid })
203 await servers[0].videos.update({ id: unlistedVideo.uuid })
205 await waitJobs(servers)
208 const { data } = await servers[2].videos.list()
209 expect(data).to.be.an('array')
210 // 1 video is unlisted
211 expect(data).to.have.lengthOf(12)
214 // Check unlisted video
215 const video = await servers[2].videos.get({ id: unlistedVideo.uuid })
216 await completeVideoCheck({ server: servers[2], originServer: servers[0], videoUUID: video.uuid, attributes: unlistedCheckAttributes })
219 it('Should send comments on a video to server 3, and automatically fetch the video', async function () {
222 await commentCommands[0].addReply({ videoId: missedVideo2.uuid, toCommentId: commentIdServer1, text: 'comment 1-3' })
224 await waitJobs(servers)
226 await servers[2].videos.get({ id: missedVideo2.uuid })
229 const { data } = await servers[2].comments.listThreads({ videoId: missedVideo2.uuid })
230 expect(data).to.be.an('array')
231 expect(data).to.have.lengthOf(1)
233 threadIdServer2 = data[0].id
235 const tree = await servers[2].comments.getThread({ videoId: missedVideo2.uuid, threadId: threadIdServer2 })
236 expect(tree.comment.text).equal('thread 1')
237 expect(tree.children).to.have.lengthOf(1)
239 const firstChild = tree.children[0]
240 expect(firstChild.comment.text).to.equal('comment 1-1')
241 expect(firstChild.children).to.have.lengthOf(1)
243 const childOfFirstChild = firstChild.children[0]
244 expect(childOfFirstChild.comment.text).to.equal('comment 1-2')
245 expect(childOfFirstChild.children).to.have.lengthOf(1)
247 const childOfChildFirstChild = childOfFirstChild.children[0]
248 expect(childOfChildFirstChild.comment.text).to.equal('comment 1-3')
249 expect(childOfChildFirstChild.children).to.have.lengthOf(0)
251 commentIdServer2 = childOfChildFirstChild.comment.id
255 it('Should correctly reply to the comment', async function () {
258 await servers[2].comments.addReply({ videoId: missedVideo2.uuid, toCommentId: commentIdServer2, text: 'comment 1-4' })
260 await waitJobs(servers)
262 const tree = await commentCommands[0].getThread({ videoId: missedVideo2.uuid, threadId: threadIdServer1 })
264 expect(tree.comment.text).equal('thread 1')
265 expect(tree.children).to.have.lengthOf(1)
267 const firstChild = tree.children[0]
268 expect(firstChild.comment.text).to.equal('comment 1-1')
269 expect(firstChild.children).to.have.lengthOf(1)
271 const childOfFirstChild = firstChild.children[0]
272 expect(childOfFirstChild.comment.text).to.equal('comment 1-2')
273 expect(childOfFirstChild.children).to.have.lengthOf(1)
275 const childOfChildFirstChild = childOfFirstChild.children[0]
276 expect(childOfChildFirstChild.comment.text).to.equal('comment 1-3')
277 expect(childOfChildFirstChild.children).to.have.lengthOf(1)
279 const childOfChildOfChildOfFirstChild = childOfChildFirstChild.children[0]
280 expect(childOfChildOfChildOfFirstChild.comment.text).to.equal('comment 1-4')
281 expect(childOfChildOfChildOfFirstChild.children).to.have.lengthOf(0)
284 it('Should upload many videos on server 1', async function () {
287 for (let i = 0; i < 10; i++) {
288 const uuid = (await servers[0].videos.quickUpload({ name: 'video ' + i })).uuid
289 videoIdsServer1.push(uuid)
292 await waitJobs(servers)
294 for (const id of videoIdsServer1) {
295 await servers[1].videos.get({ id })
298 await waitJobs(servers)
299 await sqlCommands[1].setActorFollowScores(20)
301 // Wait video expiration
304 // Refresh video -> score + 10 = 30
305 await servers[1].videos.get({ id: videoIdsServer1[0] })
307 await waitJobs(servers)
310 it('Should remove followings that are down', async function () {
313 await killallServers([ servers[0] ])
315 // Wait video expiration
318 for (let i = 0; i < 5; i++) {
320 await servers[1].videos.get({ id: videoIdsServer1[i] })
321 await waitJobs([ servers[1] ])
326 for (const id of videoIdsServer1) {
327 await servers[1].videos.get({ id, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
331 after(async function () {
332 for (const sqlCommand of sqlCommands) {
333 await sqlCommand.cleanup()
336 await cleanupTests(servers)