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