logo

pleroma

My custom branche(s) on git.pleroma.social/pleroma/pleroma git clone https://hacktivis.me/git/pleroma.git

utils_test.exs (20230B)


  1. # Pleroma: A lightweight social networking server
  2. # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
  3. # SPDX-License-Identifier: AGPL-3.0-only
  4. defmodule Pleroma.Web.ActivityPub.UtilsTest do
  5. use Pleroma.DataCase, async: true
  6. alias Pleroma.Activity
  7. alias Pleroma.Object
  8. alias Pleroma.Repo
  9. alias Pleroma.User
  10. alias Pleroma.Web.ActivityPub.Utils
  11. alias Pleroma.Web.AdminAPI.AccountView
  12. alias Pleroma.Web.CommonAPI
  13. import Pleroma.Factory
  14. require Pleroma.Constants
  15. describe "strip_report_status_data/1" do
  16. test "does not break on issues with the reported activities" do
  17. reporter = insert(:user)
  18. target_account = insert(:user)
  19. {:ok, activity} = CommonAPI.post(target_account, %{status: "foobar"})
  20. context = Utils.generate_context_id()
  21. content = "foobar"
  22. post_id = activity.data["id"]
  23. res =
  24. Utils.make_flag_data(
  25. %{
  26. actor: reporter,
  27. context: context,
  28. account: target_account,
  29. statuses: [%{"id" => post_id}],
  30. content: content
  31. },
  32. %{}
  33. )
  34. res =
  35. res
  36. |> Map.put("object", res["object"] ++ [nil, 1, 5, "123"])
  37. {:ok, activity} = Pleroma.Web.ActivityPub.ActivityPub.insert(res)
  38. [user_id, object | _] = activity.data["object"]
  39. {:ok, stripped} = Utils.strip_report_status_data(activity)
  40. assert stripped.data["object"] == [user_id, object["id"]]
  41. end
  42. end
  43. describe "fetch the latest Follow" do
  44. test "fetches the latest Follow activity" do
  45. %Activity{data: %{"type" => "Follow"}} = activity = insert(:follow_activity)
  46. follower = User.get_cached_by_ap_id(activity.data["actor"])
  47. followed = User.get_cached_by_ap_id(activity.data["object"])
  48. assert activity == Utils.fetch_latest_follow(follower, followed)
  49. end
  50. end
  51. describe "determine_explicit_mentions()" do
  52. test "works with an object that has mentions" do
  53. object = %{
  54. "tag" => [
  55. %{
  56. "type" => "Mention",
  57. "href" => "https://example.com/~alyssa",
  58. "name" => "Alyssa P. Hacker"
  59. }
  60. ]
  61. }
  62. assert Utils.determine_explicit_mentions(object) == ["https://example.com/~alyssa"]
  63. end
  64. test "works with an object that does not have mentions" do
  65. object = %{
  66. "tag" => [
  67. %{"type" => "Hashtag", "href" => "https://example.com/tag/2hu", "name" => "2hu"}
  68. ]
  69. }
  70. assert Utils.determine_explicit_mentions(object) == []
  71. end
  72. test "works with an object that has mentions and other tags" do
  73. object = %{
  74. "tag" => [
  75. %{
  76. "type" => "Mention",
  77. "href" => "https://example.com/~alyssa",
  78. "name" => "Alyssa P. Hacker"
  79. },
  80. %{"type" => "Hashtag", "href" => "https://example.com/tag/2hu", "name" => "2hu"}
  81. ]
  82. }
  83. assert Utils.determine_explicit_mentions(object) == ["https://example.com/~alyssa"]
  84. end
  85. test "works with an object that has no tags" do
  86. object = %{}
  87. assert Utils.determine_explicit_mentions(object) == []
  88. end
  89. test "works with an object that has only IR tags" do
  90. object = %{"tag" => ["2hu"]}
  91. assert Utils.determine_explicit_mentions(object) == []
  92. end
  93. test "works with an object has tags as map" do
  94. object = %{
  95. "tag" => %{
  96. "type" => "Mention",
  97. "href" => "https://example.com/~alyssa",
  98. "name" => "Alyssa P. Hacker"
  99. }
  100. }
  101. assert Utils.determine_explicit_mentions(object) == ["https://example.com/~alyssa"]
  102. end
  103. end
  104. describe "make_like_data" do
  105. setup do
  106. user = insert(:user)
  107. other_user = insert(:user)
  108. third_user = insert(:user)
  109. [user: user, other_user: other_user, third_user: third_user]
  110. end
  111. test "addresses actor's follower address if the activity is public", %{
  112. user: user,
  113. other_user: other_user,
  114. third_user: third_user
  115. } do
  116. expected_to = Enum.sort([user.ap_id, other_user.follower_address])
  117. expected_cc = Enum.sort(["https://www.w3.org/ns/activitystreams#Public", third_user.ap_id])
  118. {:ok, activity} =
  119. CommonAPI.post(user, %{
  120. status:
  121. "hey @#{other_user.nickname}, @#{third_user.nickname} how about beering together this weekend?"
  122. })
  123. %{"to" => to, "cc" => cc} = Utils.make_like_data(other_user, activity, nil)
  124. assert Enum.sort(to) == expected_to
  125. assert Enum.sort(cc) == expected_cc
  126. end
  127. test "does not address actor's follower address if the activity is not public", %{
  128. user: user,
  129. other_user: other_user,
  130. third_user: third_user
  131. } do
  132. expected_to = Enum.sort([user.ap_id])
  133. expected_cc = [third_user.ap_id]
  134. {:ok, activity} =
  135. CommonAPI.post(user, %{
  136. status: "@#{other_user.nickname} @#{third_user.nickname} bought a new swimsuit!",
  137. visibility: "private"
  138. })
  139. %{"to" => to, "cc" => cc} = Utils.make_like_data(other_user, activity, nil)
  140. assert Enum.sort(to) == expected_to
  141. assert Enum.sort(cc) == expected_cc
  142. end
  143. end
  144. test "make_json_ld_header/0" do
  145. assert Utils.make_json_ld_header() == %{
  146. "@context" => [
  147. "https://www.w3.org/ns/activitystreams",
  148. "http://localhost:4001/schemas/litepub-0.1.jsonld",
  149. %{
  150. "@language" => "und"
  151. }
  152. ]
  153. }
  154. end
  155. describe "get_existing_votes" do
  156. test "fetches existing votes" do
  157. user = insert(:user)
  158. other_user = insert(:user)
  159. {:ok, activity} =
  160. CommonAPI.post(user, %{
  161. status: "How do I pronounce LaTeX?",
  162. poll: %{
  163. options: ["laytekh", "lahtekh", "latex"],
  164. expires_in: 20,
  165. multiple: true
  166. }
  167. })
  168. object = Object.normalize(activity, fetch: false)
  169. {:ok, votes, object} = CommonAPI.vote(other_user, object, [0, 1])
  170. assert Enum.sort(Utils.get_existing_votes(other_user.ap_id, object)) == Enum.sort(votes)
  171. end
  172. test "fetches only Create activities" do
  173. user = insert(:user)
  174. other_user = insert(:user)
  175. {:ok, activity} =
  176. CommonAPI.post(user, %{
  177. status: "Are we living in a society?",
  178. poll: %{
  179. options: ["yes", "no"],
  180. expires_in: 20
  181. }
  182. })
  183. object = Object.normalize(activity, fetch: false)
  184. {:ok, [vote], object} = CommonAPI.vote(other_user, object, [0])
  185. {:ok, _activity} = CommonAPI.favorite(user, activity.id)
  186. [fetched_vote] = Utils.get_existing_votes(other_user.ap_id, object)
  187. assert fetched_vote.id == vote.id
  188. end
  189. end
  190. describe "update_follow_state_for_all/2" do
  191. test "updates the state of all Follow activities with the same actor and object" do
  192. user = insert(:user, is_locked: true)
  193. follower = insert(:user)
  194. {:ok, _, _, follow_activity} = CommonAPI.follow(follower, user)
  195. {:ok, _, _, follow_activity_two} = CommonAPI.follow(follower, user)
  196. data =
  197. follow_activity_two.data
  198. |> Map.put("state", "accept")
  199. cng = Ecto.Changeset.change(follow_activity_two, data: data)
  200. {:ok, follow_activity_two} = Repo.update(cng)
  201. {:ok, follow_activity_two} =
  202. Utils.update_follow_state_for_all(follow_activity_two, "accept")
  203. assert refresh_record(follow_activity).data["state"] == "accept"
  204. assert refresh_record(follow_activity_two).data["state"] == "accept"
  205. end
  206. test "also updates the state of accepted follows" do
  207. user = insert(:user)
  208. follower = insert(:user)
  209. {:ok, _, _, follow_activity} = CommonAPI.follow(follower, user)
  210. {:ok, _, _, follow_activity_two} = CommonAPI.follow(follower, user)
  211. {:ok, follow_activity_two} =
  212. Utils.update_follow_state_for_all(follow_activity_two, "reject")
  213. assert refresh_record(follow_activity).data["state"] == "reject"
  214. assert refresh_record(follow_activity_two).data["state"] == "reject"
  215. end
  216. end
  217. describe "update_follow_state/2" do
  218. test "updates the state of the given follow activity" do
  219. user = insert(:user, is_locked: true)
  220. follower = insert(:user)
  221. {:ok, _, _, follow_activity} = CommonAPI.follow(follower, user)
  222. {:ok, _, _, follow_activity_two} = CommonAPI.follow(follower, user)
  223. data =
  224. follow_activity_two.data
  225. |> Map.put("state", "accept")
  226. cng = Ecto.Changeset.change(follow_activity_two, data: data)
  227. {:ok, follow_activity_two} = Repo.update(cng)
  228. {:ok, follow_activity_two} = Utils.update_follow_state(follow_activity_two, "reject")
  229. assert refresh_record(follow_activity).data["state"] == "pending"
  230. assert refresh_record(follow_activity_two).data["state"] == "reject"
  231. end
  232. end
  233. describe "update_element_in_object/3" do
  234. test "updates likes" do
  235. user = insert(:user)
  236. activity = insert(:note_activity)
  237. object = Object.normalize(activity, fetch: false)
  238. assert {:ok, updated_object} =
  239. Utils.update_element_in_object(
  240. "like",
  241. [user.ap_id],
  242. object
  243. )
  244. assert updated_object.data["likes"] == [user.ap_id]
  245. assert updated_object.data["like_count"] == 1
  246. end
  247. end
  248. describe "add_like_to_object/2" do
  249. test "add actor to likes" do
  250. user = insert(:user)
  251. user2 = insert(:user)
  252. object = insert(:note)
  253. assert {:ok, updated_object} =
  254. Utils.add_like_to_object(
  255. %Activity{data: %{"actor" => user.ap_id}},
  256. object
  257. )
  258. assert updated_object.data["likes"] == [user.ap_id]
  259. assert updated_object.data["like_count"] == 1
  260. assert {:ok, updated_object2} =
  261. Utils.add_like_to_object(
  262. %Activity{data: %{"actor" => user2.ap_id}},
  263. updated_object
  264. )
  265. assert updated_object2.data["likes"] == [user2.ap_id, user.ap_id]
  266. assert updated_object2.data["like_count"] == 2
  267. end
  268. end
  269. describe "remove_like_from_object/2" do
  270. test "removes ap_id from likes" do
  271. user = insert(:user)
  272. user2 = insert(:user)
  273. object = insert(:note, data: %{"likes" => [user.ap_id, user2.ap_id], "like_count" => 2})
  274. assert {:ok, updated_object} =
  275. Utils.remove_like_from_object(
  276. %Activity{data: %{"actor" => user.ap_id}},
  277. object
  278. )
  279. assert updated_object.data["likes"] == [user2.ap_id]
  280. assert updated_object.data["like_count"] == 1
  281. end
  282. end
  283. describe "get_existing_like/2" do
  284. test "fetches existing like" do
  285. note_activity = insert(:note_activity)
  286. assert object = Object.normalize(note_activity, fetch: false)
  287. user = insert(:user)
  288. refute Utils.get_existing_like(user.ap_id, object)
  289. {:ok, like_activity} = CommonAPI.favorite(user, note_activity.id)
  290. assert ^like_activity = Utils.get_existing_like(user.ap_id, object)
  291. end
  292. end
  293. describe "get_get_existing_announce/2" do
  294. test "returns nil if announce not found" do
  295. actor = insert(:user)
  296. refute Utils.get_existing_announce(actor.ap_id, %{data: %{"id" => "test"}})
  297. end
  298. test "fetches existing announce" do
  299. note_activity = insert(:note_activity)
  300. assert object = Object.normalize(note_activity, fetch: false)
  301. actor = insert(:user)
  302. {:ok, announce} = CommonAPI.repeat(note_activity.id, actor)
  303. assert Utils.get_existing_announce(actor.ap_id, object) == announce
  304. end
  305. end
  306. describe "fetch_latest_block/2" do
  307. test "fetches last block activities" do
  308. user1 = insert(:user)
  309. user2 = insert(:user)
  310. assert {:ok, %Activity{} = _} = CommonAPI.block(user1, user2)
  311. assert {:ok, %Activity{} = _} = CommonAPI.block(user1, user2)
  312. assert {:ok, %Activity{} = activity} = CommonAPI.block(user1, user2)
  313. assert Utils.fetch_latest_block(user1, user2) == activity
  314. end
  315. end
  316. describe "recipient_in_message/3" do
  317. test "returns true when recipient in `to`" do
  318. recipient = insert(:user)
  319. actor = insert(:user)
  320. assert Utils.recipient_in_message(recipient, actor, %{"to" => recipient.ap_id})
  321. assert Utils.recipient_in_message(
  322. recipient,
  323. actor,
  324. %{"to" => [recipient.ap_id], "cc" => ""}
  325. )
  326. end
  327. test "returns true when recipient in `cc`" do
  328. recipient = insert(:user)
  329. actor = insert(:user)
  330. assert Utils.recipient_in_message(recipient, actor, %{"cc" => recipient.ap_id})
  331. assert Utils.recipient_in_message(
  332. recipient,
  333. actor,
  334. %{"cc" => [recipient.ap_id], "to" => ""}
  335. )
  336. end
  337. test "returns true when recipient in `bto`" do
  338. recipient = insert(:user)
  339. actor = insert(:user)
  340. assert Utils.recipient_in_message(recipient, actor, %{"bto" => recipient.ap_id})
  341. assert Utils.recipient_in_message(
  342. recipient,
  343. actor,
  344. %{"bcc" => "", "bto" => [recipient.ap_id]}
  345. )
  346. end
  347. test "returns true when recipient in `bcc`" do
  348. recipient = insert(:user)
  349. actor = insert(:user)
  350. assert Utils.recipient_in_message(recipient, actor, %{"bcc" => recipient.ap_id})
  351. assert Utils.recipient_in_message(
  352. recipient,
  353. actor,
  354. %{"bto" => "", "bcc" => [recipient.ap_id]}
  355. )
  356. end
  357. test "returns true when message without addresses fields" do
  358. recipient = insert(:user)
  359. actor = insert(:user)
  360. assert Utils.recipient_in_message(recipient, actor, %{"bccc" => recipient.ap_id})
  361. assert Utils.recipient_in_message(
  362. recipient,
  363. actor,
  364. %{"btod" => "", "bccc" => [recipient.ap_id]}
  365. )
  366. end
  367. test "returns false" do
  368. recipient = insert(:user)
  369. actor = insert(:user)
  370. refute Utils.recipient_in_message(recipient, actor, %{"to" => "ap_id"})
  371. end
  372. end
  373. describe "lazy_put_activity_defaults/2" do
  374. test "returns map with id and published data" do
  375. note_activity = insert(:note_activity)
  376. object = Object.normalize(note_activity, fetch: false)
  377. res = Utils.lazy_put_activity_defaults(%{"context" => object.data["id"]})
  378. assert res["context"] == object.data["id"]
  379. assert res["id"]
  380. assert res["published"]
  381. end
  382. test "returns map with fake id and published data" do
  383. assert %{
  384. "context" => "pleroma:fakecontext",
  385. "id" => "pleroma:fakeid",
  386. "published" => _
  387. } = Utils.lazy_put_activity_defaults(%{}, true)
  388. end
  389. test "returns activity data with object" do
  390. note_activity = insert(:note_activity)
  391. object = Object.normalize(note_activity, fetch: false)
  392. res =
  393. Utils.lazy_put_activity_defaults(%{
  394. "context" => object.data["id"],
  395. "object" => %{}
  396. })
  397. assert res["context"] == object.data["id"]
  398. assert res["id"]
  399. assert res["published"]
  400. assert res["object"]["id"]
  401. assert res["object"]["published"]
  402. assert res["object"]["context"] == object.data["id"]
  403. end
  404. end
  405. describe "make_flag_data" do
  406. test "returns empty map when params is invalid" do
  407. assert Utils.make_flag_data(%{}, %{}) == %{}
  408. end
  409. test "returns map with Flag object" do
  410. reporter = insert(:user)
  411. target_account = insert(:user)
  412. {:ok, activity} = CommonAPI.post(target_account, %{status: "foobar"})
  413. context = Utils.generate_context_id()
  414. content = "foobar"
  415. target_ap_id = target_account.ap_id
  416. object_ap_id = activity.object.data["id"]
  417. res =
  418. Utils.make_flag_data(
  419. %{
  420. actor: reporter,
  421. context: context,
  422. account: target_account,
  423. statuses: [%{"id" => activity.data["id"]}],
  424. content: content
  425. },
  426. %{}
  427. )
  428. note_obj = %{
  429. "type" => "Note",
  430. "id" => object_ap_id,
  431. "content" => content,
  432. "published" => activity.object.data["published"],
  433. "actor" =>
  434. AccountView.render("show.json", %{user: target_account, skip_visibility_check: true})
  435. }
  436. assert %{
  437. "type" => "Flag",
  438. "content" => ^content,
  439. "context" => ^context,
  440. "object" => [^target_ap_id, ^note_obj],
  441. "state" => "open"
  442. } = res
  443. end
  444. test "returns map with Flag object with a non-Create Activity" do
  445. reporter = insert(:user)
  446. posting_account = insert(:user)
  447. target_account = insert(:user)
  448. {:ok, activity} = CommonAPI.post(posting_account, %{status: "foobar"})
  449. {:ok, like} = CommonAPI.favorite(target_account, activity.id)
  450. context = Utils.generate_context_id()
  451. content = "foobar"
  452. target_ap_id = target_account.ap_id
  453. object_ap_id = activity.object.data["id"]
  454. res =
  455. Utils.make_flag_data(
  456. %{
  457. actor: reporter,
  458. context: context,
  459. account: target_account,
  460. statuses: [%{"id" => like.data["id"]}],
  461. content: content
  462. },
  463. %{}
  464. )
  465. note_obj = %{
  466. "type" => "Note",
  467. "id" => object_ap_id,
  468. "content" => content,
  469. "published" => activity.object.data["published"],
  470. "actor" =>
  471. AccountView.render("show.json", %{user: posting_account, skip_visibility_check: true})
  472. }
  473. assert %{
  474. "type" => "Flag",
  475. "content" => ^content,
  476. "context" => ^context,
  477. "object" => [^target_ap_id, ^note_obj],
  478. "state" => "open"
  479. } = res
  480. end
  481. end
  482. describe "add_announce_to_object/2" do
  483. test "adds actor to announcement" do
  484. user = insert(:user)
  485. object = insert(:note)
  486. activity =
  487. insert(:note_activity,
  488. data: %{
  489. "actor" => user.ap_id,
  490. "cc" => [Pleroma.Constants.as_public()]
  491. }
  492. )
  493. assert {:ok, updated_object} = Utils.add_announce_to_object(activity, object)
  494. assert updated_object.data["announcements"] == [user.ap_id]
  495. assert updated_object.data["announcement_count"] == 1
  496. end
  497. end
  498. describe "remove_announce_from_object/2" do
  499. test "removes actor from announcements" do
  500. user = insert(:user)
  501. user2 = insert(:user)
  502. object =
  503. insert(:note,
  504. data: %{"announcements" => [user.ap_id, user2.ap_id], "announcement_count" => 2}
  505. )
  506. activity = insert(:note_activity, data: %{"actor" => user.ap_id})
  507. assert {:ok, updated_object} = Utils.remove_announce_from_object(activity, object)
  508. assert updated_object.data["announcements"] == [user2.ap_id]
  509. assert updated_object.data["announcement_count"] == 1
  510. end
  511. end
  512. describe "get_cached_emoji_reactions/1" do
  513. test "returns the normalized data or an empty list" do
  514. object = insert(:note)
  515. assert Utils.get_cached_emoji_reactions(object) == []
  516. object = insert(:note, data: %{"reactions" => [["x", ["lain"]]]})
  517. assert Utils.get_cached_emoji_reactions(object) == [["x", ["lain"], nil]]
  518. object = insert(:note, data: %{"reactions" => %{}})
  519. assert Utils.get_cached_emoji_reactions(object) == []
  520. end
  521. end
  522. describe "add_emoji_reaction_to_object/1" do
  523. test "works with legacy 2-tuple format" do
  524. user = insert(:user)
  525. other_user = insert(:user)
  526. third_user = insert(:user)
  527. note =
  528. insert(:note,
  529. user: user,
  530. data: %{
  531. "reactions" => [["😿", [other_user.ap_id]]]
  532. }
  533. )
  534. _activity = insert(:note_activity, user: user, note: note)
  535. Utils.add_emoji_reaction_to_object(
  536. %Activity{data: %{"content" => "😿", "actor" => third_user.ap_id}},
  537. note
  538. )
  539. end
  540. end
  541. end