Refractor audit user identifier
[oweals/peertube.git] / server / tests / api / server / redundancy.ts
1 /* tslint:disable:no-unused-expression */
2
3 import * as chai from 'chai'
4 import 'mocha'
5 import { VideoDetails } from '../../../../shared/models/videos'
6 import {
7   doubleFollow,
8   flushAndRunMultipleServers,
9   getFollowingListPaginationAndSort,
10   getVideo,
11   immutableAssign,
12   killallServers,
13   root,
14   ServerInfo,
15   setAccessTokensToServers,
16   uploadVideo,
17   viewVideo,
18   wait
19 } from '../../utils'
20 import { waitJobs } from '../../utils/server/jobs'
21 import * as magnetUtil from 'magnet-uri'
22 import { updateRedundancy } from '../../utils/server/redundancy'
23 import { ActorFollow } from '../../../../shared/models/actors'
24 import { readdir } from 'fs-extra'
25 import { join } from 'path'
26 import { VideoRedundancyStrategy } from '../../../../shared/models/redundancy'
27 import { getStats } from '../../utils/server/stats'
28 import { ServerStats } from '../../../../shared/models/server/server-stats.model'
29
30 const expect = chai.expect
31
32 let servers: ServerInfo[] = []
33 let video1Server2UUID: string
34 let video2Server2UUID: string
35
36 function checkMagnetWebseeds (file: { magnetUri: string, resolution: { id: number } }, baseWebseeds: string[]) {
37   const parsed = magnetUtil.decode(file.magnetUri)
38
39   for (const ws of baseWebseeds) {
40     const found = parsed.urlList.find(url => url === `${ws}-${file.resolution.id}.mp4`)
41     expect(found, `Webseed ${ws} not found in ${file.magnetUri}`).to.not.be.undefined
42   }
43 }
44
45 async function runServers (strategy: VideoRedundancyStrategy, additionalParams: any = {}) {
46   const config = {
47     redundancy: {
48       videos: {
49         check_interval: '5 seconds',
50         strategies: [
51           immutableAssign({
52             strategy: strategy,
53             size: '100KB'
54           }, additionalParams)
55         ]
56       }
57     }
58   }
59   servers = await flushAndRunMultipleServers(3, config)
60
61   // Get the access tokens
62   await setAccessTokensToServers(servers)
63
64   {
65     const res = await uploadVideo(servers[ 1 ].url, servers[ 1 ].accessToken, { name: 'video 1 server 2' })
66     video1Server2UUID = res.body.video.uuid
67
68     await viewVideo(servers[ 1 ].url, video1Server2UUID)
69   }
70
71   {
72     const res = await uploadVideo(servers[ 1 ].url, servers[ 1 ].accessToken, { name: 'video 2 server 2' })
73     video2Server2UUID = res.body.video.uuid
74   }
75
76   await waitJobs(servers)
77
78   // Server 1 and server 2 follow each other
79   await doubleFollow(servers[ 0 ], servers[ 1 ])
80   // Server 1 and server 3 follow each other
81   await doubleFollow(servers[ 0 ], servers[ 2 ])
82   // Server 2 and server 3 follow each other
83   await doubleFollow(servers[ 1 ], servers[ 2 ])
84
85   await waitJobs(servers)
86 }
87
88 async function check1WebSeed (strategy: VideoRedundancyStrategy) {
89   const webseeds = [
90     'http://localhost:9002/static/webseed/' + video1Server2UUID
91   ]
92
93   for (const server of servers) {
94     {
95       const res = await getVideo(server.url, video1Server2UUID)
96
97       const video: VideoDetails = res.body
98       video.files.forEach(f => checkMagnetWebseeds(f, webseeds))
99     }
100
101     {
102       const res = await getStats(server.url)
103       const data: ServerStats = res.body
104
105       expect(data.videosRedundancy).to.have.lengthOf(1)
106
107       const stat = data.videosRedundancy[0]
108       expect(stat.strategy).to.equal(strategy)
109       expect(stat.totalSize).to.equal(102400)
110       expect(stat.totalUsed).to.equal(0)
111       expect(stat.totalVideoFiles).to.equal(0)
112       expect(stat.totalVideos).to.equal(0)
113     }
114   }
115 }
116
117 async function enableRedundancy () {
118   await updateRedundancy(servers[ 0 ].url, servers[ 0 ].accessToken, servers[ 1 ].host, true)
119
120   const res = await getFollowingListPaginationAndSort(servers[ 0 ].url, 0, 5, '-createdAt')
121   const follows: ActorFollow[] = res.body.data
122   const server2 = follows.find(f => f.following.host === 'localhost:9002')
123   const server3 = follows.find(f => f.following.host === 'localhost:9003')
124
125   expect(server3).to.not.be.undefined
126   expect(server3.following.hostRedundancyAllowed).to.be.false
127
128   expect(server2).to.not.be.undefined
129   expect(server2.following.hostRedundancyAllowed).to.be.true
130 }
131
132 async function check2Webseeds (strategy: VideoRedundancyStrategy) {
133   await waitJobs(servers)
134   await wait(15000)
135   await waitJobs(servers)
136
137   const webseeds = [
138     'http://localhost:9001/static/webseed/' + video1Server2UUID,
139     'http://localhost:9002/static/webseed/' + video1Server2UUID
140   ]
141
142   for (const server of servers) {
143     {
144       const res = await getVideo(server.url, video1Server2UUID)
145
146       const video: VideoDetails = res.body
147
148       for (const file of video.files) {
149         checkMagnetWebseeds(file, webseeds)
150       }
151     }
152   }
153
154   const files = await readdir(join(root(), 'test1', 'videos'))
155   expect(files).to.have.lengthOf(4)
156
157   for (const resolution of [ 240, 360, 480, 720 ]) {
158     expect(files.find(f => f === `${video1Server2UUID}-${resolution}.mp4`)).to.not.be.undefined
159   }
160
161   {
162     const res = await getStats(servers[0].url)
163     const data: ServerStats = res.body
164
165     expect(data.videosRedundancy).to.have.lengthOf(1)
166     const stat = data.videosRedundancy[0]
167
168     expect(stat.strategy).to.equal(strategy)
169     expect(stat.totalSize).to.equal(102400)
170     expect(stat.totalUsed).to.be.at.least(1).and.below(102401)
171     expect(stat.totalVideoFiles).to.equal(4)
172     expect(stat.totalVideos).to.equal(1)
173   }
174 }
175
176 async function cleanServers () {
177   killallServers(servers)
178 }
179
180 describe('Test videos redundancy', function () {
181
182   describe('With most-views strategy', function () {
183     const strategy = 'most-views'
184
185     before(function () {
186       this.timeout(120000)
187
188       return runServers(strategy)
189     })
190
191     it('Should have 1 webseed on the first video', function () {
192       return check1WebSeed(strategy)
193     })
194
195     it('Should enable redundancy on server 1', function () {
196       return enableRedundancy()
197     })
198
199     it('Should have 2 webseed on the first video', function () {
200       this.timeout(40000)
201
202       return check2Webseeds(strategy)
203     })
204
205     after(function () {
206       return cleanServers()
207     })
208   })
209
210   describe('With trending strategy', function () {
211     const strategy = 'trending'
212
213     before(function () {
214       this.timeout(120000)
215
216       return runServers(strategy)
217     })
218
219     it('Should have 1 webseed on the first video', function () {
220       return check1WebSeed(strategy)
221     })
222
223     it('Should enable redundancy on server 1', function () {
224       return enableRedundancy()
225     })
226
227     it('Should have 2 webseed on the first video', function () {
228       this.timeout(40000)
229
230       return check2Webseeds(strategy)
231     })
232
233     after(function () {
234       return cleanServers()
235     })
236   })
237
238   describe('With recently added strategy', function () {
239     const strategy = 'recently-added'
240
241     before(function () {
242       this.timeout(120000)
243
244       return runServers(strategy, { minViews: 3 })
245     })
246
247     it('Should have 1 webseed on the first video', function () {
248       return check1WebSeed(strategy)
249     })
250
251     it('Should enable redundancy on server 1', function () {
252       return enableRedundancy()
253     })
254
255     it('Should still have 1 webseed on the first video', async function () {
256       this.timeout(40000)
257
258       await waitJobs(servers)
259       await wait(15000)
260       await waitJobs(servers)
261
262       return check1WebSeed(strategy)
263     })
264
265     it('Should view 2 times the first video', async function () {
266       this.timeout(40000)
267
268       await viewVideo(servers[ 0 ].url, video1Server2UUID)
269       await viewVideo(servers[ 2 ].url, video1Server2UUID)
270
271       await wait(10000)
272       await waitJobs(servers)
273     })
274
275     it('Should have 2 webseed on the first video', function () {
276       this.timeout(40000)
277
278       return check2Webseeds(strategy)
279     })
280
281     after(function () {
282       return cleanServers()
283     })
284   })
285 })