]>
Commit | Line | Data |
---|---|---|
09664156 C |
1 | // /!\ Before imports /!\ |
2 | process.env.NODE_ENV = 'test' | |
3 | ||
9a27cdc2 C |
4 | import * as program from 'commander' |
5 | import { Video, VideoFile, VideoRateType } from '../../../shared' | |
09664156 | 6 | import { |
09664156 | 7 | flushAndRunMultipleServers, |
09664156 | 8 | flushTests, |
9a27cdc2 C |
9 | getAllVideosListBy, |
10 | getRequestsStats, | |
11 | getVideo, | |
09664156 | 12 | getVideosList, |
9a27cdc2 | 13 | killallServers, |
09664156 | 14 | removeVideo, |
9a27cdc2 C |
15 | ServerInfo as DefaultServerInfo, |
16 | setAccessTokensToServers, | |
17 | updateVideo, | |
18 | uploadVideo, | |
19 | wait | |
09664156 | 20 | } from '../utils' |
9a27cdc2 | 21 | import { follow } from '../utils/follows' |
09664156 C |
22 | |
23 | interface ServerInfo extends DefaultServerInfo { | |
24 | requestsNumber: number | |
25 | } | |
26 | ||
27 | program | |
28 | .option('-c, --create [weight]', 'Weight for creating videos') | |
29 | .option('-r, --remove [weight]', 'Weight for removing videos') | |
30 | .option('-u, --update [weight]', 'Weight for updating videos') | |
31 | .option('-v, --view [weight]', 'Weight for viewing videos') | |
32 | .option('-l, --like [weight]', 'Weight for liking videos') | |
33 | .option('-s, --dislike [weight]', 'Weight for disliking videos') | |
9a27cdc2 | 34 | .option('-p, --servers [n]', 'Number of servers to run (3 or 6)', /^3|6$/, 3) |
09664156 C |
35 | .option('-i, --interval-action [interval]', 'Interval in ms for an action') |
36 | .option('-I, --interval-integrity [interval]', 'Interval in ms for an integrity check') | |
37 | .option('-f, --flush', 'Flush datas on exit') | |
38 | .option('-d, --difference', 'Display difference if integrity is not okay') | |
39 | .parse(process.argv) | |
40 | ||
41 | const createWeight = program['create'] !== undefined ? parseInt(program['create'], 10) : 5 | |
42 | const removeWeight = program['remove'] !== undefined ? parseInt(program['remove'], 10) : 4 | |
43 | const updateWeight = program['update'] !== undefined ? parseInt(program['update'], 10) : 4 | |
44 | const viewWeight = program['view'] !== undefined ? parseInt(program['view'], 10) : 4 | |
45 | const likeWeight = program['like'] !== undefined ? parseInt(program['like'], 10) : 4 | |
46 | const dislikeWeight = program['dislike'] !== undefined ? parseInt(program['dislike'], 10) : 4 | |
47 | const flushAtExit = program['flush'] || false | |
48 | const actionInterval = program['intervalAction'] !== undefined ? parseInt(program['intervalAction'], 10) : 500 | |
49 | const integrityInterval = program['intervalIntegrity'] !== undefined ? parseInt(program['intervalIntegrity'], 10) : 60000 | |
50 | const displayDiffOnFail = program['difference'] || false | |
51 | ||
9a27cdc2 | 52 | const numberOfServers = 6 |
09664156 C |
53 | |
54 | console.log( | |
55 | 'Create weight: %d, update weight: %d, remove weight: %d, view weight: %d, like weight: %d, dislike weight: %d.', | |
56 | createWeight, updateWeight, removeWeight, viewWeight, likeWeight, dislikeWeight | |
57 | ) | |
58 | ||
59 | if (flushAtExit) { | |
60 | console.log('Program will flush data on exit.') | |
61 | } else { | |
62 | console.log('Program will not flush data on exit.') | |
63 | } | |
64 | if (displayDiffOnFail) { | |
65 | console.log('Program will display diff on failure.') | |
66 | } else { | |
67 | console.log('Program will not display diff on failure') | |
68 | } | |
69 | console.log('Interval in ms for each action: %d.', actionInterval) | |
70 | console.log('Interval in ms for each integrity check: %d.', integrityInterval) | |
71 | ||
72 | console.log('Run servers...') | |
73 | ||
74 | start() | |
75 | ||
76 | // ---------------------------------------------------------------------------- | |
77 | ||
78 | async function start () { | |
9a27cdc2 | 79 | const servers = await runServers(numberOfServers) |
09664156 | 80 | |
53abc4c2 C |
81 | process.on('exit', async () => { |
82 | await exitServers(servers, flushAtExit) | |
83 | ||
84 | return | |
85 | }) | |
09664156 C |
86 | process.on('SIGINT', goodbye) |
87 | process.on('SIGTERM', goodbye) | |
88 | ||
53abc4c2 | 89 | console.log('Servers ran') |
09664156 C |
90 | initializeRequestsPerServer(servers) |
91 | ||
92 | let checking = false | |
93 | ||
94 | setInterval(async () => { | |
95 | if (checking === true) return | |
96 | ||
97 | const rand = getRandomInt(0, createWeight + updateWeight + removeWeight + viewWeight + likeWeight + dislikeWeight) | |
98 | ||
99 | const numServer = getRandomNumServer(servers) | |
100 | servers[numServer].requestsNumber++ | |
101 | ||
102 | if (rand < createWeight) { | |
103 | await upload(servers, numServer) | |
104 | } else if (rand < createWeight + updateWeight) { | |
105 | await update(servers, numServer) | |
106 | } else if (rand < createWeight + updateWeight + removeWeight) { | |
107 | await remove(servers, numServer) | |
108 | } else if (rand < createWeight + updateWeight + removeWeight + viewWeight) { | |
109 | await view(servers, numServer) | |
110 | } else if (rand < createWeight + updateWeight + removeWeight + viewWeight + likeWeight) { | |
111 | await like(servers, numServer) | |
112 | } else { | |
113 | await dislike(servers, numServer) | |
114 | } | |
115 | }, actionInterval) | |
116 | ||
117 | // The function will check the consistency between servers (should have the same videos with same attributes...) | |
118 | setInterval(function () { | |
119 | if (checking === true) return | |
120 | ||
121 | console.log('Checking integrity...') | |
122 | checking = true | |
123 | ||
124 | const waitingInterval = setInterval(async () => { | |
6d33593a C |
125 | const pendingRequests = await isTherePendingRequests(servers) |
126 | if (pendingRequests === true) { | |
127 | console.log('A server has pending requests, waiting...') | |
09664156 C |
128 | return |
129 | } | |
130 | ||
6d33593a C |
131 | // Even if there are no pending request, wait some potential processes |
132 | await wait(2000) | |
09664156 C |
133 | await checkIntegrity(servers) |
134 | ||
135 | initializeRequestsPerServer(servers) | |
136 | checking = false | |
137 | clearInterval(waitingInterval) | |
7a7724e6 | 138 | }, 10000) |
09664156 C |
139 | }, integrityInterval) |
140 | } | |
141 | ||
142 | function initializeRequestsPerServer (servers: ServerInfo[]) { | |
143 | servers.forEach(server => server.requestsNumber = 0) | |
144 | } | |
145 | ||
146 | function getRandomInt (min, max) { | |
147 | return Math.floor(Math.random() * (max - min)) + min | |
148 | } | |
149 | ||
150 | function getRandomNumServer (servers) { | |
151 | return getRandomInt(0, servers.length) | |
152 | } | |
153 | ||
9a27cdc2 C |
154 | async function runServers (numberOfServers: number) { |
155 | const servers: ServerInfo[] = (await flushAndRunMultipleServers(numberOfServers)) | |
53abc4c2 | 156 | .map(s => Object.assign({ requestsNumber: 0 }, s)) |
09664156 C |
157 | |
158 | // Get the access tokens | |
159 | await setAccessTokensToServers(servers) | |
160 | ||
9a27cdc2 C |
161 | for (let i = 0; i < numberOfServers; i++) { |
162 | for (let j = 0; j < numberOfServers; j++) { | |
163 | if (i === j) continue | |
09664156 | 164 | |
9a27cdc2 C |
165 | await follow(servers[i].url, [ servers[j].url ], servers[i].accessToken) |
166 | } | |
167 | } | |
09664156 C |
168 | |
169 | return servers | |
170 | } | |
171 | ||
172 | async function exitServers (servers: ServerInfo[], flushAtExit: boolean) { | |
173 | killallServers(servers) | |
174 | ||
175 | if (flushAtExit) await flushTests() | |
176 | } | |
177 | ||
178 | function upload (servers: ServerInfo[], numServer: number) { | |
179 | console.log('Uploading video to server ' + numServer) | |
180 | ||
181 | const videoAttributes = { | |
182 | name: Date.now() + ' name', | |
183 | category: 4, | |
184 | nsfw: false, | |
185 | licence: 2, | |
186 | language: 1, | |
187 | description: Date.now() + ' description', | |
188 | tags: [ Date.now().toString().substring(0, 5) + 't1', Date.now().toString().substring(0, 5) + 't2' ], | |
189 | fixture: 'video_short1.webm' | |
190 | } | |
191 | return uploadVideo(servers[numServer].url, servers[numServer].accessToken, videoAttributes) | |
192 | } | |
193 | ||
194 | async function update (servers: ServerInfo[], numServer: number) { | |
195 | const res = await getVideosList(servers[numServer].url) | |
196 | ||
197 | const videos = res.body.data.filter(video => video.isLocal === true) | |
198 | if (videos.length === 0) return undefined | |
199 | ||
200 | const toUpdate = videos[getRandomInt(0, videos.length)].id | |
201 | const attributes = { | |
202 | name: Date.now() + ' name', | |
203 | description: Date.now() + ' description', | |
204 | tags: [ Date.now().toString().substring(0, 5) + 't1', Date.now().toString().substring(0, 5) + 't2' ] | |
205 | } | |
206 | ||
207 | console.log('Updating video of server ' + numServer) | |
208 | ||
209 | return updateVideo(servers[numServer].url, servers[numServer].accessToken, toUpdate, attributes) | |
210 | } | |
211 | ||
212 | async function remove (servers: ServerInfo[], numServer: number) { | |
213 | const res = await getVideosList(servers[numServer].url) | |
6d33593a | 214 | const videos = res.body.data.filter(video => video.isLocal === true) |
09664156 C |
215 | if (videos.length === 0) return undefined |
216 | ||
217 | const toRemove = videos[getRandomInt(0, videos.length)].id | |
218 | ||
219 | console.log('Removing video from server ' + numServer) | |
220 | return removeVideo(servers[numServer].url, servers[numServer].accessToken, toRemove) | |
221 | } | |
222 | ||
223 | async function view (servers: ServerInfo[], numServer: number) { | |
224 | const res = await getVideosList(servers[numServer].url) | |
225 | ||
226 | const videos = res.body.data | |
227 | if (videos.length === 0) return undefined | |
228 | ||
229 | const toView = videos[getRandomInt(0, videos.length)].id | |
230 | ||
231 | console.log('Viewing video from server ' + numServer) | |
232 | return getVideo(servers[numServer].url, toView) | |
233 | } | |
234 | ||
235 | function like (servers: ServerInfo[], numServer: number) { | |
236 | return rate(servers, numServer, 'like') | |
237 | } | |
238 | ||
239 | function dislike (servers: ServerInfo[], numServer: number) { | |
240 | return rate(servers, numServer, 'dislike') | |
241 | } | |
242 | ||
243 | async function rate (servers: ServerInfo[], numServer: number, rating: VideoRateType) { | |
244 | const res = await getVideosList(servers[numServer].url) | |
245 | ||
246 | const videos = res.body.data | |
247 | if (videos.length === 0) return undefined | |
248 | ||
249 | const toRate = videos[getRandomInt(0, videos.length)].id | |
250 | ||
251 | console.log('Rating (%s) video from server %d', rating, numServer) | |
252 | return getVideo(servers[numServer].url, toRate) | |
253 | } | |
254 | ||
255 | async function checkIntegrity (servers: ServerInfo[]) { | |
256 | const videos: Video[][] = [] | |
257 | const tasks: Promise<any>[] = [] | |
258 | ||
9a27cdc2 | 259 | // Fetch all videos and remove some fields that can differ between servers |
09664156 | 260 | for (const server of servers) { |
6d33593a | 261 | const p = getAllVideosListBy(server.url).then(res => videos.push(res.body.data)) |
09664156 C |
262 | tasks.push(p) |
263 | } | |
264 | ||
265 | await Promise.all(tasks) | |
266 | ||
267 | let i = 0 | |
268 | for (const video of videos) { | |
6d33593a C |
269 | const differences = areDifferences(video, videos[0]) |
270 | if (differences !== undefined) { | |
09664156 C |
271 | console.error('Integrity not ok with server %d!', i + 1) |
272 | ||
273 | if (displayDiffOnFail) { | |
6d33593a | 274 | console.log(differences) |
09664156 C |
275 | } |
276 | ||
277 | process.exit(-1) | |
278 | } | |
279 | ||
280 | i++ | |
281 | } | |
282 | ||
283 | console.log('Integrity ok.') | |
284 | } | |
285 | ||
6d33593a C |
286 | function areDifferences (videos1: Video[], videos2: Video[]) { |
287 | // Remove some keys we don't want to compare | |
288 | videos1.concat(videos2).forEach(video => { | |
289 | delete video.id | |
290 | delete video.isLocal | |
291 | delete video.thumbnailPath | |
292 | delete video.updatedAt | |
293 | delete video.views | |
294 | }) | |
295 | ||
296 | if (videos1.length !== videos2.length) { | |
297 | return `Videos length are different (${videos1.length}/${videos2.length}).` | |
298 | } | |
299 | ||
300 | for (const video1 of videos1) { | |
301 | const video2 = videos2.find(video => video.uuid === video1.uuid) | |
302 | ||
303 | if (!video2) return 'Video ' + video1.uuid + ' is missing.' | |
304 | ||
305 | for (const videoKey of Object.keys(video1)) { | |
306 | const attribute1 = video1[videoKey] | |
307 | const attribute2 = video2[videoKey] | |
308 | ||
309 | if (videoKey === 'tags') { | |
310 | if (attribute1.length !== attribute2.length) { | |
311 | return 'Tags are different.' | |
312 | } | |
313 | ||
314 | attribute1.forEach(tag1 => { | |
315 | if (attribute2.indexOf(tag1) === -1) { | |
316 | return 'Tag ' + tag1 + ' is missing.' | |
317 | } | |
318 | }) | |
319 | } else if (videoKey === 'files') { | |
320 | if (attribute1.length !== attribute2.length) { | |
321 | return 'Video files are different.' | |
322 | } | |
323 | ||
324 | attribute1.forEach((videoFile1: VideoFile) => { | |
325 | const videoFile2: VideoFile = attribute2.find(videoFile => videoFile.magnetUri === videoFile1.magnetUri) | |
326 | if (!videoFile2) { | |
327 | return `Video ${video1.uuid} has missing video file ${videoFile1.magnetUri}.` | |
328 | } | |
329 | ||
330 | if (videoFile1.size !== videoFile2.size || videoFile1.resolutionLabel !== videoFile2.resolutionLabel) { | |
331 | return `Video ${video1.uuid} has different video file ${videoFile1.magnetUri}.` | |
332 | } | |
333 | }) | |
334 | } else { | |
335 | if (attribute1 !== attribute2) { | |
336 | return `Video ${video1.uuid} has different value for attribute ${videoKey}.` | |
337 | } | |
338 | } | |
339 | } | |
340 | } | |
341 | ||
342 | return undefined | |
343 | } | |
344 | ||
09664156 C |
345 | function goodbye () { |
346 | return process.exit(-1) | |
347 | } | |
348 | ||
6d33593a | 349 | async function isTherePendingRequests (servers: ServerInfo[]) { |
09664156 | 350 | const tasks: Promise<any>[] = [] |
6d33593a | 351 | let pendingRequests = false |
09664156 | 352 | |
6d33593a | 353 | // Check if each server has pending request |
09664156 C |
354 | for (const server of servers) { |
355 | const p = getRequestsStats(server).then(res => { | |
356 | const stats = res.body | |
357 | ||
358 | if ( | |
359 | stats.requestScheduler.totalRequests !== 0 || | |
360 | stats.requestVideoEventScheduler.totalRequests !== 0 || | |
361 | stats.requestVideoQaduScheduler.totalRequests !== 0 | |
362 | ) { | |
6d33593a | 363 | pendingRequests = true |
09664156 C |
364 | } |
365 | }) | |
366 | ||
367 | tasks.push(p) | |
368 | } | |
369 | ||
370 | await Promise.all(tasks) | |
371 | ||
6d33593a | 372 | return pendingRequests |
09664156 | 373 | } |