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.

629 lines
20KB

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