]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame_incremental - scripts/benchmark.ts
Improve benchmark script CLI options
[github/Chocobozzz/PeerTube.git] / scripts / benchmark.ts
... / ...
CommitLineData
1import autocannon, { printResult } from 'autocannon'
2import { program } from 'commander'
3import { writeJson } from 'fs-extra'
4import { Video, VideoPrivacy } from '@shared/models'
5import { createSingleServer, killallServers, PeerTubeServer, setAccessTokensToServers } from '@shared/server-commands'
6
7let server: PeerTubeServer
8let video: Video
9let threadId: number
10
11program
12 .option('-o, --outfile [outfile]', 'Outfile')
13 .option('--grep [string]', 'Filter tests you want to execute')
14 .description('Run API REST benchmark')
15 .parse(process.argv)
16
17const options = program.opts()
18
19const outfile = options.outfile
20
21run()
22 .catch(err => console.error(err))
23 .finally(() => {
24 if (server) return killallServers([ server ])
25 })
26
27function buildAuthorizationHeader () {
28 return {
29 Authorization: 'Bearer ' + server.accessToken
30 }
31}
32
33function buildAPHeader () {
34 return {
35 Accept: 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'
36 }
37}
38
39async function run () {
40 console.log('Preparing server...')
41
42 await prepare()
43
44 const tests = [
45 {
46 title: 'AP - account peertube',
47 path: '/accounts/peertube',
48 headers: buildAPHeader(),
49 expecter: (body, status) => {
50 return status === 200 && body.startsWith('{"type":')
51 }
52 },
53 {
54 title: 'AP - video',
55 path: '/videos/watch/' + video.uuid,
56 headers: buildAPHeader(),
57 expecter: (body, status) => {
58 return status === 200 && body.startsWith('{"type":"Video"')
59 }
60 },
61 {
62 title: 'Misc - webfinger peertube',
63 path: '/.well-known/webfinger?resource=acct:peertube@' + server.host,
64 expecter: (body, status) => {
65 return status === 200 && body.startsWith('{"subject":')
66 }
67 },
68 {
69 title: 'API - unread notifications',
70 path: '/api/v1/users/me/notifications?start=0&count=0&unread=true',
71 headers: buildAuthorizationHeader(),
72 expecter: (_body, status) => {
73 return status === 200
74 }
75 },
76 {
77 title: 'API - me',
78 path: '/api/v1/users/me',
79 headers: buildAuthorizationHeader(),
80 expecter: (body, status) => {
81 return status === 200 && body.startsWith('{"id":')
82 }
83 },
84 {
85 title: 'API - videos list',
86 path: '/api/v1/videos',
87 expecter: (body, status) => {
88 return status === 200 && body.startsWith('{"total":10')
89 }
90 },
91 {
92 title: 'API - video get',
93 path: '/api/v1/videos/' + video.uuid,
94 expecter: (body, status) => {
95 return status === 200 && body.startsWith('{"id":')
96 }
97 },
98 {
99 title: 'API - video captions',
100 path: '/api/v1/videos/' + video.uuid + '/captions',
101 expecter: (body, status) => {
102 return status === 200 && body.startsWith('{"total":4')
103 }
104 },
105 {
106 title: 'API - video threads',
107 path: '/api/v1/videos/' + video.uuid + '/comment-threads',
108 expecter: (body, status) => {
109 return status === 200 && body.startsWith('{"total":10')
110 }
111 },
112 {
113 title: 'API - video replies',
114 path: '/api/v1/videos/' + video.uuid + '/comment-threads/' + threadId,
115 expecter: (body, status) => {
116 return status === 200 && body.startsWith('{"comment":{')
117 }
118 },
119 {
120 title: 'HTML - video watch',
121 path: '/videos/watch/' + video.uuid,
122 expecter: (body, status) => {
123 return status === 200 && body.includes('<title>my super')
124 }
125 },
126 {
127 title: 'HTML - video embed',
128 path: '/videos/embed/' + video.uuid,
129 expecter: (body, status) => {
130 return status === 200 && body.includes('embed')
131 }
132 },
133 {
134 title: 'HTML - homepage',
135 path: '/',
136 expecter: (_body, status) => {
137 return status === 200
138 }
139 },
140 {
141 title: 'API - config',
142 path: '/api/v1/config',
143 expecter: (body, status) => {
144 return status === 200 && body.startsWith('{"client":')
145 }
146 }
147 ].filter(t => {
148 if (!options.grep) return true
149
150 return t.title.includes(options.grep)
151 })
152
153 const finalResult: any[] = []
154
155 for (const test of tests) {
156 console.log('Running against %s.', test.path)
157 const testResult = await runBenchmark(test)
158
159 Object.assign(testResult, { title: test.title, path: test.path })
160 finalResult.push(testResult)
161
162 console.log(printResult(testResult))
163 }
164
165 if (outfile) await writeJson(outfile, finalResult)
166}
167
168function runBenchmark (options: {
169 path: string
170 headers?: { [ id: string ]: string }
171 expecter: Function
172}) {
173 const { path, expecter, headers } = options
174
175 return new Promise((res, rej) => {
176 autocannon({
177 url: server.url + path,
178 connections: 20,
179 headers,
180 pipelining: 1,
181 duration: 10,
182 requests: [
183 {
184 onResponse: (status, body) => {
185 if (expecter(body, status) !== true) {
186 console.error('Expected result failed.', { body, status })
187 throw new Error('Invalid expectation')
188 }
189 }
190 }
191 ]
192 }, (err, result) => {
193 if (err) return rej(err)
194
195 return res(result)
196 })
197 })
198}
199
200async function prepare () {
201 server = await createSingleServer(1, {
202 rates_limit: {
203 api: {
204 max: 5_000_000
205 }
206 }
207 })
208 await setAccessTokensToServers([ server ])
209
210 const attributes = {
211 name: 'my super video',
212 category: 2,
213 nsfw: true,
214 licence: 6,
215 language: 'fr',
216 privacy: VideoPrivacy.PUBLIC,
217 support: 'please give me a coffee',
218 description: 'my super description'.repeat(10),
219 tags: [ 'tag1', 'tag2', 'tag3' ]
220 }
221
222 for (let i = 0; i < 10; i++) {
223 await server.videos.upload({ attributes: { ...attributes, name: 'my super video ' + i } })
224 }
225
226 const { data } = await server.videos.list()
227 video = data.find(v => v.name === 'my super video 1')
228
229 for (let i = 0; i < 10; i++) {
230 const text = 'my super first comment'
231 const created = await server.comments.createThread({ videoId: video.id, text })
232 threadId = created.id
233
234 const text1 = 'my super answer to thread 1'
235 const child = await server.comments.addReply({ videoId: video.id, toCommentId: threadId, text: text1 })
236
237 const text2 = 'my super answer to answer of thread 1'
238 await server.comments.addReply({ videoId: video.id, toCommentId: child.id, text: text2 })
239
240 const text3 = 'my second answer to thread 1'
241 await server.comments.addReply({ videoId: video.id, toCommentId: threadId, text: text3 })
242 }
243
244 for (const caption of [ 'ar', 'fr', 'en', 'zh' ]) {
245 await server.captions.add({
246 language: caption,
247 videoId: video.id,
248 fixture: 'subtitle-good2.vtt'
249 })
250 }
251
252 return { server, video, threadId }
253}