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.

514 lines
16KB

  1. # Pleroma: A lightweight social networking server
  2. # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
  3. # SPDX-License-Identifier: AGPL-3.0-only
  4. defmodule Pleroma.Web.CommonAPITest do
  5. use Pleroma.DataCase
  6. alias Pleroma.Activity
  7. alias Pleroma.Conversation.Participation
  8. alias Pleroma.Object
  9. alias Pleroma.User
  10. alias Pleroma.Web.ActivityPub.ActivityPub
  11. alias Pleroma.Web.ActivityPub.Visibility
  12. alias Pleroma.Web.CommonAPI
  13. import Pleroma.Factory
  14. clear_config([:instance, :safe_dm_mentions])
  15. clear_config([:instance, :limit])
  16. clear_config([:instance, :max_pinned_statuses])
  17. test "when replying to a conversation / participation, it will set the correct context id even if no explicit reply_to is given" do
  18. user = insert(:user)
  19. {:ok, activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "direct"})
  20. [participation] = Participation.for_user(user)
  21. {:ok, convo_reply} =
  22. CommonAPI.post(user, %{"status" => ".", "in_reply_to_conversation_id" => participation.id})
  23. assert Visibility.is_direct?(convo_reply)
  24. assert activity.data["context"] == convo_reply.data["context"]
  25. end
  26. test "when replying to a conversation / participation, it only mentions the recipients explicitly declared in the participation" do
  27. har = insert(:user)
  28. jafnhar = insert(:user)
  29. tridi = insert(:user)
  30. {:ok, activity} =
  31. CommonAPI.post(har, %{
  32. "status" => "@#{jafnhar.nickname} hey",
  33. "visibility" => "direct"
  34. })
  35. assert har.ap_id in activity.recipients
  36. assert jafnhar.ap_id in activity.recipients
  37. [participation] = Participation.for_user(har)
  38. {:ok, activity} =
  39. CommonAPI.post(har, %{
  40. "status" => "I don't really like @#{tridi.nickname}",
  41. "visibility" => "direct",
  42. "in_reply_to_status_id" => activity.id,
  43. "in_reply_to_conversation_id" => participation.id
  44. })
  45. assert har.ap_id in activity.recipients
  46. assert jafnhar.ap_id in activity.recipients
  47. refute tridi.ap_id in activity.recipients
  48. end
  49. test "with the safe_dm_mention option set, it does not mention people beyond the initial tags" do
  50. har = insert(:user)
  51. jafnhar = insert(:user)
  52. tridi = insert(:user)
  53. Pleroma.Config.put([:instance, :safe_dm_mentions], true)
  54. {:ok, activity} =
  55. CommonAPI.post(har, %{
  56. "status" => "@#{jafnhar.nickname} hey, i never want to see @#{tridi.nickname} again",
  57. "visibility" => "direct"
  58. })
  59. refute tridi.ap_id in activity.recipients
  60. assert jafnhar.ap_id in activity.recipients
  61. end
  62. test "it de-duplicates tags" do
  63. user = insert(:user)
  64. {:ok, activity} = CommonAPI.post(user, %{"status" => "#2hu #2HU"})
  65. object = Object.normalize(activity)
  66. assert object.data["tag"] == ["2hu"]
  67. end
  68. test "it adds emoji in the object" do
  69. user = insert(:user)
  70. {:ok, activity} = CommonAPI.post(user, %{"status" => ":firefox:"})
  71. assert Object.normalize(activity).data["emoji"]["firefox"]
  72. end
  73. test "it adds emoji when updating profiles" do
  74. user = insert(:user, %{name: ":firefox:"})
  75. CommonAPI.update(user)
  76. user = User.get_cached_by_ap_id(user.ap_id)
  77. [firefox] = user.info.source_data["tag"]
  78. assert firefox["name"] == ":firefox:"
  79. end
  80. describe "posting" do
  81. test "it supports explicit addressing" do
  82. user = insert(:user)
  83. user_two = insert(:user)
  84. user_three = insert(:user)
  85. user_four = insert(:user)
  86. {:ok, activity} =
  87. CommonAPI.post(user, %{
  88. "status" =>
  89. "Hey, I think @#{user_three.nickname} is ugly. @#{user_four.nickname} is alright though.",
  90. "to" => [user_two.nickname, user_four.nickname, "nonexistent"]
  91. })
  92. assert user.ap_id in activity.recipients
  93. assert user_two.ap_id in activity.recipients
  94. assert user_four.ap_id in activity.recipients
  95. refute user_three.ap_id in activity.recipients
  96. end
  97. test "it filters out obviously bad tags when accepting a post as HTML" do
  98. user = insert(:user)
  99. post = "<p><b>2hu</b></p><script>alert('xss')</script>"
  100. {:ok, activity} =
  101. CommonAPI.post(user, %{
  102. "status" => post,
  103. "content_type" => "text/html"
  104. })
  105. object = Object.normalize(activity)
  106. assert object.data["content"] == "<p><b>2hu</b></p>alert('xss')"
  107. end
  108. test "it filters out obviously bad tags when accepting a post as Markdown" do
  109. user = insert(:user)
  110. post = "<p><b>2hu</b></p><script>alert('xss')</script>"
  111. {:ok, activity} =
  112. CommonAPI.post(user, %{
  113. "status" => post,
  114. "content_type" => "text/markdown"
  115. })
  116. object = Object.normalize(activity)
  117. assert object.data["content"] == "<p><b>2hu</b></p>alert('xss')"
  118. end
  119. test "it does not allow replies to direct messages that are not direct messages themselves" do
  120. user = insert(:user)
  121. {:ok, activity} = CommonAPI.post(user, %{"status" => "suya..", "visibility" => "direct"})
  122. assert {:ok, _} =
  123. CommonAPI.post(user, %{
  124. "status" => "suya..",
  125. "visibility" => "direct",
  126. "in_reply_to_status_id" => activity.id
  127. })
  128. Enum.each(["public", "private", "unlisted"], fn visibility ->
  129. assert {:error, "The message visibility must be direct"} =
  130. CommonAPI.post(user, %{
  131. "status" => "suya..",
  132. "visibility" => visibility,
  133. "in_reply_to_status_id" => activity.id
  134. })
  135. end)
  136. end
  137. test "it allows to address a list" do
  138. user = insert(:user)
  139. {:ok, list} = Pleroma.List.create("foo", user)
  140. {:ok, activity} =
  141. CommonAPI.post(user, %{"status" => "foobar", "visibility" => "list:#{list.id}"})
  142. assert activity.data["bcc"] == [list.ap_id]
  143. assert activity.recipients == [list.ap_id, user.ap_id]
  144. assert activity.data["listMessage"] == list.ap_id
  145. end
  146. test "it returns error when status is empty and no attachments" do
  147. user = insert(:user)
  148. assert {:error, "Cannot post an empty status without attachments"} =
  149. CommonAPI.post(user, %{"status" => ""})
  150. end
  151. test "it returns error when character limit is exceeded" do
  152. Pleroma.Config.put([:instance, :limit], 5)
  153. user = insert(:user)
  154. assert {:error, "The status is over the character limit"} =
  155. CommonAPI.post(user, %{"status" => "foobar"})
  156. end
  157. test "it can handle activities that expire" do
  158. user = insert(:user)
  159. expires_at =
  160. NaiveDateTime.utc_now()
  161. |> NaiveDateTime.truncate(:second)
  162. |> NaiveDateTime.add(1_000_000, :second)
  163. assert {:ok, activity} =
  164. CommonAPI.post(user, %{"status" => "chai", "expires_in" => 1_000_000})
  165. assert expiration = Pleroma.ActivityExpiration.get_by_activity_id(activity.id)
  166. assert expiration.scheduled_at == expires_at
  167. end
  168. end
  169. describe "reactions" do
  170. test "repeating a status" do
  171. user = insert(:user)
  172. other_user = insert(:user)
  173. {:ok, activity} = CommonAPI.post(other_user, %{"status" => "cofe"})
  174. {:ok, %Activity{}, _} = CommonAPI.repeat(activity.id, user)
  175. end
  176. test "favoriting a status" do
  177. user = insert(:user)
  178. other_user = insert(:user)
  179. {:ok, activity} = CommonAPI.post(other_user, %{"status" => "cofe"})
  180. {:ok, %Activity{}, _} = CommonAPI.favorite(activity.id, user)
  181. end
  182. test "retweeting a status twice returns an error" do
  183. user = insert(:user)
  184. other_user = insert(:user)
  185. {:ok, activity} = CommonAPI.post(other_user, %{"status" => "cofe"})
  186. {:ok, %Activity{}, _object} = CommonAPI.repeat(activity.id, user)
  187. {:error, _} = CommonAPI.repeat(activity.id, user)
  188. end
  189. test "favoriting a status twice returns an error" do
  190. user = insert(:user)
  191. other_user = insert(:user)
  192. {:ok, activity} = CommonAPI.post(other_user, %{"status" => "cofe"})
  193. {:ok, %Activity{}, _object} = CommonAPI.favorite(activity.id, user)
  194. {:error, _} = CommonAPI.favorite(activity.id, user)
  195. end
  196. end
  197. describe "pinned statuses" do
  198. setup do
  199. Pleroma.Config.put([:instance, :max_pinned_statuses], 1)
  200. user = insert(:user)
  201. {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
  202. [user: user, activity: activity]
  203. end
  204. test "pin status", %{user: user, activity: activity} do
  205. assert {:ok, ^activity} = CommonAPI.pin(activity.id, user)
  206. id = activity.id
  207. user = refresh_record(user)
  208. assert %User{info: %{pinned_activities: [^id]}} = user
  209. end
  210. test "unlisted statuses can be pinned", %{user: user} do
  211. {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!", "visibility" => "unlisted"})
  212. assert {:ok, ^activity} = CommonAPI.pin(activity.id, user)
  213. end
  214. test "only self-authored can be pinned", %{activity: activity} do
  215. user = insert(:user)
  216. assert {:error, "Could not pin"} = CommonAPI.pin(activity.id, user)
  217. end
  218. test "max pinned statuses", %{user: user, activity: activity_one} do
  219. {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
  220. assert {:ok, ^activity_one} = CommonAPI.pin(activity_one.id, user)
  221. user = refresh_record(user)
  222. assert {:error, "You have already pinned the maximum number of statuses"} =
  223. CommonAPI.pin(activity_two.id, user)
  224. end
  225. test "unpin status", %{user: user, activity: activity} do
  226. {:ok, activity} = CommonAPI.pin(activity.id, user)
  227. user = refresh_record(user)
  228. assert {:ok, ^activity} = CommonAPI.unpin(activity.id, user)
  229. user = refresh_record(user)
  230. assert %User{info: %{pinned_activities: []}} = user
  231. end
  232. test "should unpin when deleting a status", %{user: user, activity: activity} do
  233. {:ok, activity} = CommonAPI.pin(activity.id, user)
  234. user = refresh_record(user)
  235. assert {:ok, _} = CommonAPI.delete(activity.id, user)
  236. user = refresh_record(user)
  237. assert %User{info: %{pinned_activities: []}} = user
  238. end
  239. end
  240. describe "mute tests" do
  241. setup do
  242. user = insert(:user)
  243. activity = insert(:note_activity)
  244. [user: user, activity: activity]
  245. end
  246. test "add mute", %{user: user, activity: activity} do
  247. {:ok, _} = CommonAPI.add_mute(user, activity)
  248. assert CommonAPI.thread_muted?(user, activity)
  249. end
  250. test "remove mute", %{user: user, activity: activity} do
  251. CommonAPI.add_mute(user, activity)
  252. {:ok, _} = CommonAPI.remove_mute(user, activity)
  253. refute CommonAPI.thread_muted?(user, activity)
  254. end
  255. test "check that mutes can't be duplicate", %{user: user, activity: activity} do
  256. CommonAPI.add_mute(user, activity)
  257. {:error, _} = CommonAPI.add_mute(user, activity)
  258. end
  259. end
  260. describe "reports" do
  261. test "creates a report" do
  262. reporter = insert(:user)
  263. target_user = insert(:user)
  264. {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
  265. reporter_ap_id = reporter.ap_id
  266. target_ap_id = target_user.ap_id
  267. activity_ap_id = activity.data["id"]
  268. comment = "foobar"
  269. report_data = %{
  270. "account_id" => target_user.id,
  271. "comment" => comment,
  272. "status_ids" => [activity.id]
  273. }
  274. assert {:ok, flag_activity} = CommonAPI.report(reporter, report_data)
  275. assert %Activity{
  276. actor: ^reporter_ap_id,
  277. data: %{
  278. "type" => "Flag",
  279. "content" => ^comment,
  280. "object" => [^target_ap_id, ^activity_ap_id],
  281. "state" => "open"
  282. }
  283. } = flag_activity
  284. end
  285. test "updates report state" do
  286. [reporter, target_user] = insert_pair(:user)
  287. activity = insert(:note_activity, user: target_user)
  288. {:ok, %Activity{id: report_id}} =
  289. CommonAPI.report(reporter, %{
  290. "account_id" => target_user.id,
  291. "comment" => "I feel offended",
  292. "status_ids" => [activity.id]
  293. })
  294. {:ok, report} = CommonAPI.update_report_state(report_id, "resolved")
  295. assert report.data["state"] == "resolved"
  296. end
  297. test "does not update report state when state is unsupported" do
  298. [reporter, target_user] = insert_pair(:user)
  299. activity = insert(:note_activity, user: target_user)
  300. {:ok, %Activity{id: report_id}} =
  301. CommonAPI.report(reporter, %{
  302. "account_id" => target_user.id,
  303. "comment" => "I feel offended",
  304. "status_ids" => [activity.id]
  305. })
  306. assert CommonAPI.update_report_state(report_id, "test") == {:error, "Unsupported state"}
  307. end
  308. end
  309. describe "reblog muting" do
  310. setup do
  311. muter = insert(:user)
  312. muted = insert(:user)
  313. [muter: muter, muted: muted]
  314. end
  315. test "add a reblog mute", %{muter: muter, muted: muted} do
  316. {:ok, muter} = CommonAPI.hide_reblogs(muter, muted)
  317. assert User.showing_reblogs?(muter, muted) == false
  318. end
  319. test "remove a reblog mute", %{muter: muter, muted: muted} do
  320. {:ok, muter} = CommonAPI.hide_reblogs(muter, muted)
  321. {:ok, muter} = CommonAPI.show_reblogs(muter, muted)
  322. assert User.showing_reblogs?(muter, muted) == true
  323. end
  324. end
  325. describe "unfollow/2" do
  326. test "also unsubscribes a user" do
  327. [follower, followed] = insert_pair(:user)
  328. {:ok, follower, followed, _} = CommonAPI.follow(follower, followed)
  329. {:ok, followed} = User.subscribe(follower, followed)
  330. assert User.subscribed_to?(follower, followed)
  331. {:ok, follower} = CommonAPI.unfollow(follower, followed)
  332. refute User.subscribed_to?(follower, followed)
  333. end
  334. end
  335. describe "accept_follow_request/2" do
  336. test "after acceptance, it sets all existing pending follow request states to 'accept'" do
  337. user = insert(:user, info: %{locked: true})
  338. follower = insert(:user)
  339. follower_two = insert(:user)
  340. {:ok, follow_activity} = ActivityPub.follow(follower, user)
  341. {:ok, follow_activity_two} = ActivityPub.follow(follower, user)
  342. {:ok, follow_activity_three} = ActivityPub.follow(follower_two, user)
  343. assert follow_activity.data["state"] == "pending"
  344. assert follow_activity_two.data["state"] == "pending"
  345. assert follow_activity_three.data["state"] == "pending"
  346. {:ok, _follower} = CommonAPI.accept_follow_request(follower, user)
  347. assert Repo.get(Activity, follow_activity.id).data["state"] == "accept"
  348. assert Repo.get(Activity, follow_activity_two.id).data["state"] == "accept"
  349. assert Repo.get(Activity, follow_activity_three.id).data["state"] == "pending"
  350. end
  351. test "after rejection, it sets all existing pending follow request states to 'reject'" do
  352. user = insert(:user, info: %{locked: true})
  353. follower = insert(:user)
  354. follower_two = insert(:user)
  355. {:ok, follow_activity} = ActivityPub.follow(follower, user)
  356. {:ok, follow_activity_two} = ActivityPub.follow(follower, user)
  357. {:ok, follow_activity_three} = ActivityPub.follow(follower_two, user)
  358. assert follow_activity.data["state"] == "pending"
  359. assert follow_activity_two.data["state"] == "pending"
  360. assert follow_activity_three.data["state"] == "pending"
  361. {:ok, _follower} = CommonAPI.reject_follow_request(follower, user)
  362. assert Repo.get(Activity, follow_activity.id).data["state"] == "reject"
  363. assert Repo.get(Activity, follow_activity_two.id).data["state"] == "reject"
  364. assert Repo.get(Activity, follow_activity_three.id).data["state"] == "pending"
  365. end
  366. end
  367. describe "vote/3" do
  368. test "does not allow to vote twice" do
  369. user = insert(:user)
  370. other_user = insert(:user)
  371. {:ok, activity} =
  372. CommonAPI.post(user, %{
  373. "status" => "Am I cute?",
  374. "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
  375. })
  376. object = Object.normalize(activity)
  377. {:ok, _, object} = CommonAPI.vote(other_user, object, [0])
  378. assert {:error, "Already voted"} == CommonAPI.vote(other_user, object, [1])
  379. end
  380. end
  381. end