]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - scripts/benchmark.ts
Improve benchmark script CLI options
[github/Chocobozzz/PeerTube.git] / scripts / benchmark.ts
CommitLineData
736c64ca 1import autocannon, { printResult } from 'autocannon'
10874276 2import { program } from 'commander'
4abbeff5 3import { writeJson } from 'fs-extra'
12edc149 4import { Video, VideoPrivacy } from '@shared/models'
f8360396 5import { createSingleServer, killallServers, PeerTubeServer, setAccessTokensToServers } from '@shared/server-commands'
4abbeff5 6
254d3579 7let server: PeerTubeServer
4abbeff5
C
8let video: Video
9let threadId: number
10
10874276
C
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
4abbeff5
C
20
21run()
22 .catch(err => console.error(err))
23 .finally(() => {
9293139f 24 if (server) return killallServers([ server ])
4abbeff5
C
25 })
26
27function buildAuthorizationHeader () {
28 return {
29 Authorization: 'Bearer ' + server.accessToken
30 }
31}
32
0ce8d34e
C
33function buildAPHeader () {
34 return {
35 Accept: 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'
36 }
37}
38
4abbeff5
C
39async function run () {
40 console.log('Preparing server...')
41
42 await prepare()
43
44 const tests = [
0ce8d34e
C
45 {
46 title: 'AP - account peertube',
47 path: '/accounts/peertube',
48 headers: buildAPHeader(),
47099aba
C
49 expecter: (body, status) => {
50 return status === 200 && body.startsWith('{"type":')
0ce8d34e
C
51 }
52 },
53 {
54 title: 'AP - video',
55 path: '/videos/watch/' + video.uuid,
56 headers: buildAPHeader(),
47099aba
C
57 expecter: (body, status) => {
58 return status === 200 && body.startsWith('{"type":"Video"')
0ce8d34e
C
59 }
60 },
61 {
62 title: 'Misc - webfinger peertube',
63 path: '/.well-known/webfinger?resource=acct:peertube@' + server.host,
47099aba
C
64 expecter: (body, status) => {
65 return status === 200 && body.startsWith('{"subject":')
0ce8d34e
C
66 }
67 },
4abbeff5
C
68 {
69 title: 'API - unread notifications',
70 path: '/api/v1/users/me/notifications?start=0&count=0&unread=true',
71 headers: buildAuthorizationHeader(),
47099aba
C
72 expecter: (_body, status) => {
73 return status === 200
4abbeff5
C
74 }
75 },
76 {
77 title: 'API - me',
78 path: '/api/v1/users/me',
79 headers: buildAuthorizationHeader(),
47099aba
C
80 expecter: (body, status) => {
81 return status === 200 && body.startsWith('{"id":')
4abbeff5
C
82 }
83 },
84 {
85 title: 'API - videos list',
86 path: '/api/v1/videos',
47099aba
C
87 expecter: (body, status) => {
88 return status === 200 && body.startsWith('{"total":10')
4abbeff5
C
89 }
90 },
91 {
92 title: 'API - video get',
93 path: '/api/v1/videos/' + video.uuid,
47099aba
C
94 expecter: (body, status) => {
95 return status === 200 && body.startsWith('{"id":')
4abbeff5
C
96 }
97 },
98 {
99 title: 'API - video captions',
100 path: '/api/v1/videos/' + video.uuid + '/captions',
47099aba
C
101 expecter: (body, status) => {
102 return status === 200 && body.startsWith('{"total":4')
4abbeff5
C
103 }
104 },
105 {
106 title: 'API - video threads',
107 path: '/api/v1/videos/' + video.uuid + '/comment-threads',
47099aba
C
108 expecter: (body, status) => {
109 return status === 200 && body.startsWith('{"total":10')
4abbeff5
C
110 }
111 },
112 {
113 title: 'API - video replies',
114 path: '/api/v1/videos/' + video.uuid + '/comment-threads/' + threadId,
47099aba
C
115 expecter: (body, status) => {
116 return status === 200 && body.startsWith('{"comment":{')
4abbeff5
C
117 }
118 },
119 {
120 title: 'HTML - video watch',
121 path: '/videos/watch/' + video.uuid,
47099aba
C
122 expecter: (body, status) => {
123 return status === 200 && body.includes('<title>my super')
4abbeff5
C
124 }
125 },
0ce8d34e
C
126 {
127 title: 'HTML - video embed',
128 path: '/videos/embed/' + video.uuid,
47099aba
C
129 expecter: (body, status) => {
130 return status === 200 && body.includes('embed')
0ce8d34e
C
131 }
132 },
4abbeff5
C
133 {
134 title: 'HTML - homepage',
135 path: '/',
47099aba
C
136 expecter: (_body, status) => {
137 return status === 200
4abbeff5
C
138 }
139 },
140 {
141 title: 'API - config',
142 path: '/api/v1/config',
47099aba 143 expecter: (body, status) => {
cf0c8ee5 144 return status === 200 && body.startsWith('{"client":')
4abbeff5
C
145 }
146 }
10874276
C
147 ].filter(t => {
148 if (!options.grep) return true
149
150 return t.title.includes(options.grep)
151 })
4abbeff5
C
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
736c64ca 162 console.log(printResult(testResult))
4abbeff5
C
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) => {
47099aba 176 autocannon({
4abbeff5
C
177 url: server.url + path,
178 connections: 20,
179 headers,
180 pipelining: 1,
47099aba
C
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 ]
4abbeff5
C
192 }, (err, result) => {
193 if (err) return rej(err)
194
195 return res(result)
196 })
4abbeff5
C
197 })
198}
199
200async function prepare () {
254d3579 201 server = await createSingleServer(1, {
4abbeff5
C
202 rates_limit: {
203 api: {
204 max: 5_000_000
205 }
206 }
207 })
208 await setAccessTokensToServers([ server ])
209
d23dd9fb 210 const attributes = {
4abbeff5
C
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++) {
89d241a7 223 await server.videos.upload({ attributes: { ...attributes, name: 'my super video ' + i } })
4abbeff5
C
224 }
225
89d241a7 226 const { data } = await server.videos.list()
d23dd9fb 227 video = data.find(v => v.name === 'my super video 1')
4abbeff5
C
228
229 for (let i = 0; i < 10; i++) {
230 const text = 'my super first comment'
89d241a7 231 const created = await server.comments.createThread({ videoId: video.id, text })
12edc149 232 threadId = created.id
4abbeff5
C
233
234 const text1 = 'my super answer to thread 1'
89d241a7 235 const child = await server.comments.addReply({ videoId: video.id, toCommentId: threadId, text: text1 })
4abbeff5
C
236
237 const text2 = 'my super answer to answer of thread 1'
89d241a7 238 await server.comments.addReply({ videoId: video.id, toCommentId: child.id, text: text2 })
4abbeff5
C
239
240 const text3 = 'my second answer to thread 1'
89d241a7 241 await server.comments.addReply({ videoId: video.id, toCommentId: threadId, text: text3 })
4abbeff5
C
242 }
243
244 for (const caption of [ 'ar', 'fr', 'en', 'zh' ]) {
c63830f1 245 await server.captions.add({
4abbeff5
C
246 language: caption,
247 videoId: video.id,
248 fixture: 'subtitle-good2.vtt'
249 })
250 }
251
252 return { server, video, threadId }
253}