Fork of Pleroma with site-specific changes and feature branches https://git.pleroma.social/pleroma/pleroma
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

353 lines
9.4KB

  1. defmodule Pleroma.LoadTesting.Generator do
  2. use Pleroma.LoadTesting.Helper
  3. alias Pleroma.Web.CommonAPI
  4. def generate_users(opts) do
  5. IO.puts("Starting generating #{opts[:users_max]} users...")
  6. {time, _} = :timer.tc(fn -> do_generate_users(opts) end)
  7. IO.puts("Inserting users take #{to_sec(time)} sec.\n")
  8. end
  9. defp do_generate_users(opts) do
  10. max = Keyword.get(opts, :users_max)
  11. Task.async_stream(
  12. 1..max,
  13. &generate_user_data(&1),
  14. max_concurrency: 10,
  15. timeout: 30_000
  16. )
  17. |> Enum.to_list()
  18. end
  19. defp generate_user_data(i) do
  20. remote = Enum.random([true, false])
  21. user = %User{
  22. name: "Test テスト User #{i}",
  23. email: "user#{i}@example.com",
  24. nickname: "nick#{i}",
  25. password_hash:
  26. "$pbkdf2-sha512$160000$bU.OSFI7H/yqWb5DPEqyjw$uKp/2rmXw12QqnRRTqTtuk2DTwZfF8VR4MYW2xMeIlqPR/UX1nT1CEKVUx2CowFMZ5JON8aDvURrZpJjSgqXrg",
  27. bio: "Tester Number #{i}",
  28. info: %{},
  29. local: remote
  30. }
  31. user_urls =
  32. if remote do
  33. base_url =
  34. Enum.random(["https://domain1.com", "https://domain2.com", "https://domain3.com"])
  35. ap_id = "#{base_url}/users/#{user.nickname}"
  36. %{
  37. ap_id: ap_id,
  38. follower_address: ap_id <> "/followers",
  39. following_address: ap_id <> "/following",
  40. following: [ap_id]
  41. }
  42. else
  43. %{
  44. ap_id: User.ap_id(user),
  45. follower_address: User.ap_followers(user),
  46. following_address: User.ap_following(user),
  47. following: [User.ap_id(user)]
  48. }
  49. end
  50. user = Map.merge(user, user_urls)
  51. Repo.insert!(user)
  52. end
  53. def generate_activities(user, users) do
  54. do_generate_activities(user, users)
  55. end
  56. defp do_generate_activities(user, users) do
  57. IO.puts("Starting generating 20000 common activities...")
  58. {time, _} =
  59. :timer.tc(fn ->
  60. Task.async_stream(
  61. 1..20_000,
  62. fn _ ->
  63. do_generate_activity([user | users])
  64. end,
  65. max_concurrency: 10,
  66. timeout: 30_000
  67. )
  68. |> Stream.run()
  69. end)
  70. IO.puts("Inserting common activities take #{to_sec(time)} sec.\n")
  71. IO.puts("Starting generating 20000 activities with mentions...")
  72. {time, _} =
  73. :timer.tc(fn ->
  74. Task.async_stream(
  75. 1..20_000,
  76. fn _ ->
  77. do_generate_activity_with_mention(user, users)
  78. end,
  79. max_concurrency: 10,
  80. timeout: 30_000
  81. )
  82. |> Stream.run()
  83. end)
  84. IO.puts("Inserting activities with menthions take #{to_sec(time)} sec.\n")
  85. IO.puts("Starting generating 10000 activities with threads...")
  86. {time, _} =
  87. :timer.tc(fn ->
  88. Task.async_stream(
  89. 1..10_000,
  90. fn _ ->
  91. do_generate_threads([user | users])
  92. end,
  93. max_concurrency: 10,
  94. timeout: 30_000
  95. )
  96. |> Stream.run()
  97. end)
  98. IO.puts("Inserting activities with threads take #{to_sec(time)} sec.\n")
  99. end
  100. defp do_generate_activity(users) do
  101. post = %{
  102. "status" => "Some status without mention with random user"
  103. }
  104. CommonAPI.post(Enum.random(users), post)
  105. end
  106. defp do_generate_activity_with_mention(user, users) do
  107. mentions_cnt = Enum.random([2, 3, 4, 5])
  108. with_user = Enum.random([true, false])
  109. users = Enum.shuffle(users)
  110. mentions_users = Enum.take(users, mentions_cnt)
  111. mentions_users = if with_user, do: [user | mentions_users], else: mentions_users
  112. mentions_str =
  113. Enum.map(mentions_users, fn user -> "@" <> user.nickname end) |> Enum.join(", ")
  114. post = %{
  115. "status" => mentions_str <> "some status with mentions random users"
  116. }
  117. CommonAPI.post(Enum.random(users), post)
  118. end
  119. defp do_generate_threads(users) do
  120. thread_length = Enum.random([2, 3, 4, 5])
  121. actor = Enum.random(users)
  122. post = %{
  123. "status" => "Start of the thread"
  124. }
  125. {:ok, activity} = CommonAPI.post(actor, post)
  126. Enum.each(1..thread_length, fn _ ->
  127. user = Enum.random(users)
  128. post = %{
  129. "status" => "@#{actor.nickname} reply to thread",
  130. "in_reply_to_status_id" => activity.id
  131. }
  132. CommonAPI.post(user, post)
  133. end)
  134. end
  135. def generate_remote_activities(user, users) do
  136. do_generate_remote_activities(user, users)
  137. end
  138. defp do_generate_remote_activities(user, users) do
  139. IO.puts("Starting generating 10000 remote activities...")
  140. {time, _} =
  141. :timer.tc(fn ->
  142. Task.async_stream(
  143. 1..10_000,
  144. fn i ->
  145. do_generate_remote_activity(i, user, users)
  146. end,
  147. max_concurrency: 10,
  148. timeout: 30_000
  149. )
  150. |> Stream.run()
  151. end)
  152. IO.puts("Inserting remote activities take #{to_sec(time)} sec.\n")
  153. end
  154. defp do_generate_remote_activity(i, user, users) do
  155. actor = Enum.random(users)
  156. %{host: host} = URI.parse(actor.ap_id)
  157. date = Date.utc_today()
  158. datetime = DateTime.utc_now()
  159. map = %{
  160. "actor" => actor.ap_id,
  161. "cc" => [actor.follower_address, user.ap_id],
  162. "context" => "tag:mastodon.example.org,#{date}:objectId=#{i}:objectType=Conversation",
  163. "id" => actor.ap_id <> "/statuses/#{i}/activity",
  164. "object" => %{
  165. "actor" => actor.ap_id,
  166. "atomUri" => actor.ap_id <> "/statuses/#{i}",
  167. "attachment" => [],
  168. "attributedTo" => actor.ap_id,
  169. "bcc" => [],
  170. "bto" => [],
  171. "cc" => [actor.follower_address, user.ap_id],
  172. "content" =>
  173. "<p><span class=\"h-card\"><a href=\"" <>
  174. user.ap_id <>
  175. "\" class=\"u-url mention\">@<span>" <> user.nickname <> "</span></a></span></p>",
  176. "context" => "tag:mastodon.example.org,#{date}:objectId=#{i}:objectType=Conversation",
  177. "conversation" =>
  178. "tag:mastodon.example.org,#{date}:objectId=#{i}:objectType=Conversation",
  179. "emoji" => %{},
  180. "id" => actor.ap_id <> "/statuses/#{i}",
  181. "inReplyTo" => nil,
  182. "inReplyToAtomUri" => nil,
  183. "published" => datetime,
  184. "sensitive" => true,
  185. "summary" => "cw",
  186. "tag" => [
  187. %{
  188. "href" => user.ap_id,
  189. "name" => "@#{user.nickname}@#{host}",
  190. "type" => "Mention"
  191. }
  192. ],
  193. "to" => ["https://www.w3.org/ns/activitystreams#Public"],
  194. "type" => "Note",
  195. "url" => "http://#{host}/@#{actor.nickname}/#{i}"
  196. },
  197. "published" => datetime,
  198. "to" => ["https://www.w3.org/ns/activitystreams#Public"],
  199. "type" => "Create"
  200. }
  201. Pleroma.Web.ActivityPub.ActivityPub.insert(map, false)
  202. end
  203. def generate_dms(user, users, opts) do
  204. IO.puts("Starting generating #{opts[:dms_max]} DMs")
  205. {time, _} = :timer.tc(fn -> do_generate_dms(user, users, opts) end)
  206. IO.puts("Inserting dms take #{to_sec(time)} sec.\n")
  207. end
  208. defp do_generate_dms(user, users, opts) do
  209. Task.async_stream(
  210. 1..opts[:dms_max],
  211. fn _ ->
  212. do_generate_dm(user, users)
  213. end,
  214. max_concurrency: 10,
  215. timeout: 30_000
  216. )
  217. |> Stream.run()
  218. end
  219. defp do_generate_dm(user, users) do
  220. post = %{
  221. "status" => "@#{user.nickname} some direct message",
  222. "visibility" => "direct"
  223. }
  224. CommonAPI.post(Enum.random(users), post)
  225. end
  226. def generate_long_thread(user, users, opts) do
  227. IO.puts("Starting generating long thread with #{opts[:thread_length]} replies")
  228. {time, activity} = :timer.tc(fn -> do_generate_long_thread(user, users, opts) end)
  229. IO.puts("Inserting long thread replies take #{to_sec(time)} sec.\n")
  230. {:ok, activity}
  231. end
  232. defp do_generate_long_thread(user, users, opts) do
  233. {:ok, %{id: id} = activity} = CommonAPI.post(user, %{"status" => "Start of long thread"})
  234. Task.async_stream(
  235. 1..opts[:thread_length],
  236. fn _ -> do_generate_thread(users, id) end,
  237. max_concurrency: 10,
  238. timeout: 30_000
  239. )
  240. |> Stream.run()
  241. activity
  242. end
  243. defp do_generate_thread(users, activity_id) do
  244. CommonAPI.post(Enum.random(users), %{
  245. "status" => "reply to main post",
  246. "in_reply_to_status_id" => activity_id
  247. })
  248. end
  249. def generate_non_visible_message(user, users) do
  250. IO.puts("Starting generating 1000 non visible posts")
  251. {time, _} =
  252. :timer.tc(fn ->
  253. do_generate_non_visible_posts(user, users)
  254. end)
  255. IO.puts("Inserting non visible posts take #{to_sec(time)} sec.\n")
  256. end
  257. defp do_generate_non_visible_posts(user, users) do
  258. [not_friend | users] = users
  259. make_friends(user, users)
  260. Task.async_stream(1..1000, fn _ -> do_generate_non_visible_post(not_friend, users) end,
  261. max_concurrency: 10,
  262. timeout: 30_000
  263. )
  264. |> Stream.run()
  265. end
  266. defp make_friends(_user, []), do: nil
  267. defp make_friends(user, [friend | users]) do
  268. {:ok, _} = User.follow(user, friend)
  269. {:ok, _} = User.follow(friend, user)
  270. make_friends(user, users)
  271. end
  272. defp do_generate_non_visible_post(not_friend, users) do
  273. post = %{
  274. "status" => "some non visible post",
  275. "visibility" => "private"
  276. }
  277. {:ok, activity} = CommonAPI.post(not_friend, post)
  278. thread_length = Enum.random([2, 3, 4, 5])
  279. Enum.each(1..thread_length, fn _ ->
  280. user = Enum.random(users)
  281. post = %{
  282. "status" => "@#{not_friend.nickname} reply to non visible post",
  283. "in_reply_to_status_id" => activity.id,
  284. "visibility" => "private"
  285. }
  286. CommonAPI.post(user, post)
  287. end)
  288. end
  289. end