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