benchmark.ts 6.3 KB

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