logo

pleroma

My custom branche(s) on git.pleroma.social/pleroma/pleroma

activity_pub_test.exs (69856B)


  1. # Pleroma: A lightweight social networking server
  2. # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
  3. # SPDX-License-Identifier: AGPL-3.0-only
  4. defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
  5. use Pleroma.DataCase
  6. use Oban.Testing, repo: Pleroma.Repo
  7. alias Pleroma.Activity
  8. alias Pleroma.Builders.ActivityBuilder
  9. alias Pleroma.Config
  10. alias Pleroma.Notification
  11. alias Pleroma.Object
  12. alias Pleroma.User
  13. alias Pleroma.Web.ActivityPub.ActivityPub
  14. alias Pleroma.Web.ActivityPub.Utils
  15. alias Pleroma.Web.AdminAPI.AccountView
  16. alias Pleroma.Web.CommonAPI
  17. import ExUnit.CaptureLog
  18. import Mock
  19. import Pleroma.Factory
  20. import Tesla.Mock
  21. setup do
  22. mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
  23. :ok
  24. end
  25. setup do: clear_config([:instance, :federating])
  26. describe "streaming out participations" do
  27. test "it streams them out" do
  28. user = insert(:user)
  29. {:ok, activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"})
  30. {:ok, conversation} = Pleroma.Conversation.create_or_bump_for(activity)
  31. participations =
  32. conversation.participations
  33. |> Repo.preload(:user)
  34. with_mock Pleroma.Web.Streamer,
  35. stream: fn _, _ -> nil end do
  36. ActivityPub.stream_out_participations(conversation.participations)
  37. assert called(Pleroma.Web.Streamer.stream("participation", participations))
  38. end
  39. end
  40. test "streams them out on activity creation" do
  41. user_one = insert(:user)
  42. user_two = insert(:user)
  43. with_mock Pleroma.Web.Streamer,
  44. stream: fn _, _ -> nil end do
  45. {:ok, activity} =
  46. CommonAPI.post(user_one, %{
  47. status: "@#{user_two.nickname}",
  48. visibility: "direct"
  49. })
  50. conversation =
  51. activity.data["context"]
  52. |> Pleroma.Conversation.get_for_ap_id()
  53. |> Repo.preload(participations: :user)
  54. assert called(Pleroma.Web.Streamer.stream("participation", conversation.participations))
  55. end
  56. end
  57. end
  58. describe "fetching restricted by visibility" do
  59. test "it restricts by the appropriate visibility" do
  60. user = insert(:user)
  61. {:ok, public_activity} = CommonAPI.post(user, %{status: ".", visibility: "public"})
  62. {:ok, direct_activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"})
  63. {:ok, unlisted_activity} = CommonAPI.post(user, %{status: ".", visibility: "unlisted"})
  64. {:ok, private_activity} = CommonAPI.post(user, %{status: ".", visibility: "private"})
  65. activities = ActivityPub.fetch_activities([], %{visibility: "direct", actor_id: user.ap_id})
  66. assert activities == [direct_activity]
  67. activities =
  68. ActivityPub.fetch_activities([], %{visibility: "unlisted", actor_id: user.ap_id})
  69. assert activities == [unlisted_activity]
  70. activities =
  71. ActivityPub.fetch_activities([], %{visibility: "private", actor_id: user.ap_id})
  72. assert activities == [private_activity]
  73. activities = ActivityPub.fetch_activities([], %{visibility: "public", actor_id: user.ap_id})
  74. assert activities == [public_activity]
  75. activities =
  76. ActivityPub.fetch_activities([], %{
  77. visibility: ~w[private public],
  78. actor_id: user.ap_id
  79. })
  80. assert activities == [public_activity, private_activity]
  81. end
  82. end
  83. describe "fetching excluded by visibility" do
  84. test "it excludes by the appropriate visibility" do
  85. user = insert(:user)
  86. {:ok, public_activity} = CommonAPI.post(user, %{status: ".", visibility: "public"})
  87. {:ok, direct_activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"})
  88. {:ok, unlisted_activity} = CommonAPI.post(user, %{status: ".", visibility: "unlisted"})
  89. {:ok, private_activity} = CommonAPI.post(user, %{status: ".", visibility: "private"})
  90. activities =
  91. ActivityPub.fetch_activities([], %{
  92. exclude_visibilities: "direct",
  93. actor_id: user.ap_id
  94. })
  95. assert public_activity in activities
  96. assert unlisted_activity in activities
  97. assert private_activity in activities
  98. refute direct_activity in activities
  99. activities =
  100. ActivityPub.fetch_activities([], %{
  101. exclude_visibilities: "unlisted",
  102. actor_id: user.ap_id
  103. })
  104. assert public_activity in activities
  105. refute unlisted_activity in activities
  106. assert private_activity in activities
  107. assert direct_activity in activities
  108. activities =
  109. ActivityPub.fetch_activities([], %{
  110. exclude_visibilities: "private",
  111. actor_id: user.ap_id
  112. })
  113. assert public_activity in activities
  114. assert unlisted_activity in activities
  115. refute private_activity in activities
  116. assert direct_activity in activities
  117. activities =
  118. ActivityPub.fetch_activities([], %{
  119. exclude_visibilities: "public",
  120. actor_id: user.ap_id
  121. })
  122. refute public_activity in activities
  123. assert unlisted_activity in activities
  124. assert private_activity in activities
  125. assert direct_activity in activities
  126. end
  127. end
  128. describe "building a user from his ap id" do
  129. test "it returns a user" do
  130. user_id = "http://mastodon.example.org/users/admin"
  131. {:ok, user} = ActivityPub.make_user_from_ap_id(user_id)
  132. assert user.ap_id == user_id
  133. assert user.nickname == "admin@mastodon.example.org"
  134. assert user.ap_enabled
  135. assert user.follower_address == "http://mastodon.example.org/users/admin/followers"
  136. end
  137. test "it returns a user that is invisible" do
  138. user_id = "http://mastodon.example.org/users/relay"
  139. {:ok, user} = ActivityPub.make_user_from_ap_id(user_id)
  140. assert User.invisible?(user)
  141. end
  142. test "it returns a user that accepts chat messages" do
  143. user_id = "http://mastodon.example.org/users/admin"
  144. {:ok, user} = ActivityPub.make_user_from_ap_id(user_id)
  145. assert user.accepts_chat_messages
  146. end
  147. end
  148. test "it fetches the appropriate tag-restricted posts" do
  149. user = insert(:user)
  150. {:ok, status_one} = CommonAPI.post(user, %{status: ". #test"})
  151. {:ok, status_two} = CommonAPI.post(user, %{status: ". #essais"})
  152. {:ok, status_three} = CommonAPI.post(user, %{status: ". #test #reject"})
  153. fetch_one = ActivityPub.fetch_activities([], %{type: "Create", tag: "test"})
  154. fetch_two = ActivityPub.fetch_activities([], %{type: "Create", tag: ["test", "essais"]})
  155. fetch_three =
  156. ActivityPub.fetch_activities([], %{
  157. type: "Create",
  158. tag: ["test", "essais"],
  159. tag_reject: ["reject"]
  160. })
  161. fetch_four =
  162. ActivityPub.fetch_activities([], %{
  163. type: "Create",
  164. tag: ["test"],
  165. tag_all: ["test", "reject"]
  166. })
  167. assert fetch_one == [status_one, status_three]
  168. assert fetch_two == [status_one, status_two, status_three]
  169. assert fetch_three == [status_one, status_two]
  170. assert fetch_four == [status_three]
  171. end
  172. describe "insertion" do
  173. test "drops activities beyond a certain limit" do
  174. limit = Config.get([:instance, :remote_limit])
  175. random_text =
  176. :crypto.strong_rand_bytes(limit + 1)
  177. |> Base.encode64()
  178. |> binary_part(0, limit + 1)
  179. data = %{
  180. "ok" => true,
  181. "object" => %{
  182. "content" => random_text
  183. }
  184. }
  185. assert {:error, :remote_limit} = ActivityPub.insert(data)
  186. end
  187. test "doesn't drop activities with content being null" do
  188. user = insert(:user)
  189. data = %{
  190. "actor" => user.ap_id,
  191. "to" => [],
  192. "object" => %{
  193. "actor" => user.ap_id,
  194. "to" => [],
  195. "type" => "Note",
  196. "content" => nil
  197. }
  198. }
  199. assert {:ok, _} = ActivityPub.insert(data)
  200. end
  201. test "returns the activity if one with the same id is already in" do
  202. activity = insert(:note_activity)
  203. {:ok, new_activity} = ActivityPub.insert(activity.data)
  204. assert activity.id == new_activity.id
  205. end
  206. test "inserts a given map into the activity database, giving it an id if it has none." do
  207. user = insert(:user)
  208. data = %{
  209. "actor" => user.ap_id,
  210. "to" => [],
  211. "object" => %{
  212. "actor" => user.ap_id,
  213. "to" => [],
  214. "type" => "Note",
  215. "content" => "hey"
  216. }
  217. }
  218. {:ok, %Activity{} = activity} = ActivityPub.insert(data)
  219. assert activity.data["ok"] == data["ok"]
  220. assert is_binary(activity.data["id"])
  221. given_id = "bla"
  222. data = %{
  223. "id" => given_id,
  224. "actor" => user.ap_id,
  225. "to" => [],
  226. "context" => "blabla",
  227. "object" => %{
  228. "actor" => user.ap_id,
  229. "to" => [],
  230. "type" => "Note",
  231. "content" => "hey"
  232. }
  233. }
  234. {:ok, %Activity{} = activity} = ActivityPub.insert(data)
  235. assert activity.data["ok"] == data["ok"]
  236. assert activity.data["id"] == given_id
  237. assert activity.data["context"] == "blabla"
  238. assert activity.data["context_id"]
  239. end
  240. test "adds a context when none is there" do
  241. user = insert(:user)
  242. data = %{
  243. "actor" => user.ap_id,
  244. "to" => [],
  245. "object" => %{
  246. "actor" => user.ap_id,
  247. "to" => [],
  248. "type" => "Note",
  249. "content" => "hey"
  250. }
  251. }
  252. {:ok, %Activity{} = activity} = ActivityPub.insert(data)
  253. object = Pleroma.Object.normalize(activity)
  254. assert is_binary(activity.data["context"])
  255. assert is_binary(object.data["context"])
  256. assert activity.data["context_id"]
  257. assert object.data["context_id"]
  258. end
  259. test "adds an id to a given object if it lacks one and is a note and inserts it to the object database" do
  260. user = insert(:user)
  261. data = %{
  262. "actor" => user.ap_id,
  263. "to" => [],
  264. "object" => %{
  265. "actor" => user.ap_id,
  266. "to" => [],
  267. "type" => "Note",
  268. "content" => "hey"
  269. }
  270. }
  271. {:ok, %Activity{} = activity} = ActivityPub.insert(data)
  272. assert object = Object.normalize(activity)
  273. assert is_binary(object.data["id"])
  274. end
  275. end
  276. describe "listen activities" do
  277. test "does not increase user note count" do
  278. user = insert(:user)
  279. {:ok, activity} =
  280. ActivityPub.listen(%{
  281. to: ["https://www.w3.org/ns/activitystreams#Public"],
  282. actor: user,
  283. context: "",
  284. object: %{
  285. "actor" => user.ap_id,
  286. "to" => ["https://www.w3.org/ns/activitystreams#Public"],
  287. "artist" => "lain",
  288. "title" => "lain radio episode 1",
  289. "length" => 180_000,
  290. "type" => "Audio"
  291. }
  292. })
  293. assert activity.actor == user.ap_id
  294. user = User.get_cached_by_id(user.id)
  295. assert user.note_count == 0
  296. end
  297. test "can be fetched into a timeline" do
  298. _listen_activity_1 = insert(:listen)
  299. _listen_activity_2 = insert(:listen)
  300. _listen_activity_3 = insert(:listen)
  301. timeline = ActivityPub.fetch_activities([], %{type: ["Listen"]})
  302. assert length(timeline) == 3
  303. end
  304. end
  305. describe "create activities" do
  306. setup do
  307. [user: insert(:user)]
  308. end
  309. test "it reverts create", %{user: user} do
  310. with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
  311. assert {:error, :reverted} =
  312. ActivityPub.create(%{
  313. to: ["user1", "user2"],
  314. actor: user,
  315. context: "",
  316. object: %{
  317. "to" => ["user1", "user2"],
  318. "type" => "Note",
  319. "content" => "testing"
  320. }
  321. })
  322. end
  323. assert Repo.aggregate(Activity, :count, :id) == 0
  324. assert Repo.aggregate(Object, :count, :id) == 0
  325. end
  326. test "creates activity if expiration is not configured and expires_at is not passed", %{
  327. user: user
  328. } do
  329. clear_config([Pleroma.Workers.PurgeExpiredActivity, :enabled], false)
  330. assert {:ok, _} =
  331. ActivityPub.create(%{
  332. to: ["user1", "user2"],
  333. actor: user,
  334. context: "",
  335. object: %{
  336. "to" => ["user1", "user2"],
  337. "type" => "Note",
  338. "content" => "testing"
  339. }
  340. })
  341. end
  342. test "rejects activity if expires_at present but expiration is not configured", %{user: user} do
  343. clear_config([Pleroma.Workers.PurgeExpiredActivity, :enabled], false)
  344. assert {:error, :expired_activities_disabled} =
  345. ActivityPub.create(%{
  346. to: ["user1", "user2"],
  347. actor: user,
  348. context: "",
  349. object: %{
  350. "to" => ["user1", "user2"],
  351. "type" => "Note",
  352. "content" => "testing"
  353. },
  354. additional: %{
  355. "expires_at" => DateTime.utc_now()
  356. }
  357. })
  358. assert Repo.aggregate(Activity, :count, :id) == 0
  359. assert Repo.aggregate(Object, :count, :id) == 0
  360. end
  361. test "removes doubled 'to' recipients", %{user: user} do
  362. {:ok, activity} =
  363. ActivityPub.create(%{
  364. to: ["user1", "user1", "user2"],
  365. actor: user,
  366. context: "",
  367. object: %{
  368. "to" => ["user1", "user1", "user2"],
  369. "type" => "Note",
  370. "content" => "testing"
  371. }
  372. })
  373. assert activity.data["to"] == ["user1", "user2"]
  374. assert activity.actor == user.ap_id
  375. assert activity.recipients == ["user1", "user2", user.ap_id]
  376. end
  377. test "increases user note count only for public activities", %{user: user} do
  378. {:ok, _} =
  379. CommonAPI.post(User.get_cached_by_id(user.id), %{
  380. status: "1",
  381. visibility: "public"
  382. })
  383. {:ok, _} =
  384. CommonAPI.post(User.get_cached_by_id(user.id), %{
  385. status: "2",
  386. visibility: "unlisted"
  387. })
  388. {:ok, _} =
  389. CommonAPI.post(User.get_cached_by_id(user.id), %{
  390. status: "2",
  391. visibility: "private"
  392. })
  393. {:ok, _} =
  394. CommonAPI.post(User.get_cached_by_id(user.id), %{
  395. status: "3",
  396. visibility: "direct"
  397. })
  398. user = User.get_cached_by_id(user.id)
  399. assert user.note_count == 2
  400. end
  401. test "increases replies count", %{user: user} do
  402. user2 = insert(:user)
  403. {:ok, activity} = CommonAPI.post(user, %{status: "1", visibility: "public"})
  404. ap_id = activity.data["id"]
  405. reply_data = %{status: "1", in_reply_to_status_id: activity.id}
  406. # public
  407. {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, :visibility, "public"))
  408. assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
  409. assert object.data["repliesCount"] == 1
  410. # unlisted
  411. {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, :visibility, "unlisted"))
  412. assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
  413. assert object.data["repliesCount"] == 2
  414. # private
  415. {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, :visibility, "private"))
  416. assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
  417. assert object.data["repliesCount"] == 2
  418. # direct
  419. {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, :visibility, "direct"))
  420. assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
  421. assert object.data["repliesCount"] == 2
  422. end
  423. end
  424. describe "fetch activities for recipients" do
  425. test "retrieve the activities for certain recipients" do
  426. {:ok, activity_one} = ActivityBuilder.insert(%{"to" => ["someone"]})
  427. {:ok, activity_two} = ActivityBuilder.insert(%{"to" => ["someone_else"]})
  428. {:ok, _activity_three} = ActivityBuilder.insert(%{"to" => ["noone"]})
  429. activities = ActivityPub.fetch_activities(["someone", "someone_else"])
  430. assert length(activities) == 2
  431. assert activities == [activity_one, activity_two]
  432. end
  433. end
  434. describe "fetch activities in context" do
  435. test "retrieves activities that have a given context" do
  436. {:ok, activity} = ActivityBuilder.insert(%{"type" => "Create", "context" => "2hu"})
  437. {:ok, activity_two} = ActivityBuilder.insert(%{"type" => "Create", "context" => "2hu"})
  438. {:ok, _activity_three} = ActivityBuilder.insert(%{"type" => "Create", "context" => "3hu"})
  439. {:ok, _activity_four} = ActivityBuilder.insert(%{"type" => "Announce", "context" => "2hu"})
  440. activity_five = insert(:note_activity)
  441. user = insert(:user)
  442. {:ok, _user_relationship} = User.block(user, %{ap_id: activity_five.data["actor"]})
  443. activities = ActivityPub.fetch_activities_for_context("2hu", %{blocking_user: user})
  444. assert activities == [activity_two, activity]
  445. end
  446. test "doesn't return activities with filtered words" do
  447. user = insert(:user)
  448. user_two = insert(:user)
  449. insert(:filter, user: user, phrase: "test", hide: true)
  450. {:ok, %{id: id1, data: %{"context" => context}}} = CommonAPI.post(user, %{status: "1"})
  451. {:ok, %{id: id2}} = CommonAPI.post(user_two, %{status: "2", in_reply_to_status_id: id1})
  452. {:ok, %{id: id3} = user_activity} =
  453. CommonAPI.post(user, %{status: "3 test?", in_reply_to_status_id: id2})
  454. {:ok, %{id: id4} = filtered_activity} =
  455. CommonAPI.post(user_two, %{status: "4 test!", in_reply_to_status_id: id3})
  456. {:ok, _} = CommonAPI.post(user, %{status: "5", in_reply_to_status_id: id4})
  457. activities =
  458. context
  459. |> ActivityPub.fetch_activities_for_context(%{user: user})
  460. |> Enum.map(& &1.id)
  461. assert length(activities) == 4
  462. assert user_activity.id in activities
  463. refute filtered_activity.id in activities
  464. end
  465. end
  466. test "doesn't return blocked activities" do
  467. activity_one = insert(:note_activity)
  468. activity_two = insert(:note_activity)
  469. activity_three = insert(:note_activity)
  470. user = insert(:user)
  471. booster = insert(:user)
  472. {:ok, _user_relationship} = User.block(user, %{ap_id: activity_one.data["actor"]})
  473. activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true})
  474. assert Enum.member?(activities, activity_two)
  475. assert Enum.member?(activities, activity_three)
  476. refute Enum.member?(activities, activity_one)
  477. {:ok, _user_block} = User.unblock(user, %{ap_id: activity_one.data["actor"]})
  478. activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true})
  479. assert Enum.member?(activities, activity_two)
  480. assert Enum.member?(activities, activity_three)
  481. assert Enum.member?(activities, activity_one)
  482. {:ok, _user_relationship} = User.block(user, %{ap_id: activity_three.data["actor"]})
  483. {:ok, %{data: %{"object" => id}}} = CommonAPI.repeat(activity_three.id, booster)
  484. %Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)
  485. activity_three = Activity.get_by_id(activity_three.id)
  486. activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true})
  487. assert Enum.member?(activities, activity_two)
  488. refute Enum.member?(activities, activity_three)
  489. refute Enum.member?(activities, boost_activity)
  490. assert Enum.member?(activities, activity_one)
  491. activities = ActivityPub.fetch_activities([], %{blocking_user: nil, skip_preload: true})
  492. assert Enum.member?(activities, activity_two)
  493. assert Enum.member?(activities, activity_three)
  494. assert Enum.member?(activities, boost_activity)
  495. assert Enum.member?(activities, activity_one)
  496. end
  497. test "doesn't return transitive interactions concerning blocked users" do
  498. blocker = insert(:user)
  499. blockee = insert(:user)
  500. friend = insert(:user)
  501. {:ok, _user_relationship} = User.block(blocker, blockee)
  502. {:ok, activity_one} = CommonAPI.post(friend, %{status: "hey!"})
  503. {:ok, activity_two} = CommonAPI.post(friend, %{status: "hey! @#{blockee.nickname}"})
  504. {:ok, activity_three} = CommonAPI.post(blockee, %{status: "hey! @#{friend.nickname}"})
  505. {:ok, activity_four} = CommonAPI.post(blockee, %{status: "hey! @#{blocker.nickname}"})
  506. activities = ActivityPub.fetch_activities([], %{blocking_user: blocker})
  507. assert Enum.member?(activities, activity_one)
  508. refute Enum.member?(activities, activity_two)
  509. refute Enum.member?(activities, activity_three)
  510. refute Enum.member?(activities, activity_four)
  511. end
  512. test "doesn't return announce activities with blocked users in 'to'" do
  513. blocker = insert(:user)
  514. blockee = insert(:user)
  515. friend = insert(:user)
  516. {:ok, _user_relationship} = User.block(blocker, blockee)
  517. {:ok, activity_one} = CommonAPI.post(friend, %{status: "hey!"})
  518. {:ok, activity_two} = CommonAPI.post(blockee, %{status: "hey! @#{friend.nickname}"})
  519. {:ok, activity_three} = CommonAPI.repeat(activity_two.id, friend)
  520. activities =
  521. ActivityPub.fetch_activities([], %{blocking_user: blocker})
  522. |> Enum.map(fn act -> act.id end)
  523. assert Enum.member?(activities, activity_one.id)
  524. refute Enum.member?(activities, activity_two.id)
  525. refute Enum.member?(activities, activity_three.id)
  526. end
  527. test "doesn't return announce activities with blocked users in 'cc'" do
  528. blocker = insert(:user)
  529. blockee = insert(:user)
  530. friend = insert(:user)
  531. {:ok, _user_relationship} = User.block(blocker, blockee)
  532. {:ok, activity_one} = CommonAPI.post(friend, %{status: "hey!"})
  533. {:ok, activity_two} = CommonAPI.post(blockee, %{status: "hey! @#{friend.nickname}"})
  534. assert object = Pleroma.Object.normalize(activity_two)
  535. data = %{
  536. "actor" => friend.ap_id,
  537. "object" => object.data["id"],
  538. "context" => object.data["context"],
  539. "type" => "Announce",
  540. "to" => ["https://www.w3.org/ns/activitystreams#Public"],
  541. "cc" => [blockee.ap_id]
  542. }
  543. assert {:ok, activity_three} = ActivityPub.insert(data)
  544. activities =
  545. ActivityPub.fetch_activities([], %{blocking_user: blocker})
  546. |> Enum.map(fn act -> act.id end)
  547. assert Enum.member?(activities, activity_one.id)
  548. refute Enum.member?(activities, activity_two.id)
  549. refute Enum.member?(activities, activity_three.id)
  550. end
  551. test "doesn't return activities from blocked domains" do
  552. domain = "dogwhistle.zone"
  553. domain_user = insert(:user, %{ap_id: "https://#{domain}/@pundit"})
  554. note = insert(:note, %{data: %{"actor" => domain_user.ap_id}})
  555. activity = insert(:note_activity, %{note: note})
  556. user = insert(:user)
  557. {:ok, user} = User.block_domain(user, domain)
  558. activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true})
  559. refute activity in activities
  560. followed_user = insert(:user)
  561. CommonAPI.follow(user, followed_user)
  562. {:ok, repeat_activity} = CommonAPI.repeat(activity.id, followed_user)
  563. activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true})
  564. refute repeat_activity in activities
  565. end
  566. test "does return activities from followed users on blocked domains" do
  567. domain = "meanies.social"
  568. domain_user = insert(:user, %{ap_id: "https://#{domain}/@pundit"})
  569. blocker = insert(:user)
  570. {:ok, blocker} = User.follow(blocker, domain_user)
  571. {:ok, blocker} = User.block_domain(blocker, domain)
  572. assert User.following?(blocker, domain_user)
  573. assert User.blocks_domain?(blocker, domain_user)
  574. refute User.blocks?(blocker, domain_user)
  575. note = insert(:note, %{data: %{"actor" => domain_user.ap_id}})
  576. activity = insert(:note_activity, %{note: note})
  577. activities = ActivityPub.fetch_activities([], %{blocking_user: blocker, skip_preload: true})
  578. assert activity in activities
  579. # And check that if the guy we DO follow boosts someone else from their domain,
  580. # that should be hidden
  581. another_user = insert(:user, %{ap_id: "https://#{domain}/@meanie2"})
  582. bad_note = insert(:note, %{data: %{"actor" => another_user.ap_id}})
  583. bad_activity = insert(:note_activity, %{note: bad_note})
  584. {:ok, repeat_activity} = CommonAPI.repeat(bad_activity.id, domain_user)
  585. activities = ActivityPub.fetch_activities([], %{blocking_user: blocker, skip_preload: true})
  586. refute repeat_activity in activities
  587. end
  588. test "doesn't return muted activities" do
  589. activity_one = insert(:note_activity)
  590. activity_two = insert(:note_activity)
  591. activity_three = insert(:note_activity)
  592. user = insert(:user)
  593. booster = insert(:user)
  594. activity_one_actor = User.get_by_ap_id(activity_one.data["actor"])
  595. {:ok, _user_relationships} = User.mute(user, activity_one_actor)
  596. activities = ActivityPub.fetch_activities([], %{muting_user: user, skip_preload: true})
  597. assert Enum.member?(activities, activity_two)
  598. assert Enum.member?(activities, activity_three)
  599. refute Enum.member?(activities, activity_one)
  600. # Calling with 'with_muted' will deliver muted activities, too.
  601. activities =
  602. ActivityPub.fetch_activities([], %{
  603. muting_user: user,
  604. with_muted: true,
  605. skip_preload: true
  606. })
  607. assert Enum.member?(activities, activity_two)
  608. assert Enum.member?(activities, activity_three)
  609. assert Enum.member?(activities, activity_one)
  610. {:ok, _user_mute} = User.unmute(user, activity_one_actor)
  611. activities = ActivityPub.fetch_activities([], %{muting_user: user, skip_preload: true})
  612. assert Enum.member?(activities, activity_two)
  613. assert Enum.member?(activities, activity_three)
  614. assert Enum.member?(activities, activity_one)
  615. activity_three_actor = User.get_by_ap_id(activity_three.data["actor"])
  616. {:ok, _user_relationships} = User.mute(user, activity_three_actor)
  617. {:ok, %{data: %{"object" => id}}} = CommonAPI.repeat(activity_three.id, booster)
  618. %Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)
  619. activity_three = Activity.get_by_id(activity_three.id)
  620. activities = ActivityPub.fetch_activities([], %{muting_user: user, skip_preload: true})
  621. assert Enum.member?(activities, activity_two)
  622. refute Enum.member?(activities, activity_three)
  623. refute Enum.member?(activities, boost_activity)
  624. assert Enum.member?(activities, activity_one)
  625. activities = ActivityPub.fetch_activities([], %{muting_user: nil, skip_preload: true})
  626. assert Enum.member?(activities, activity_two)
  627. assert Enum.member?(activities, activity_three)
  628. assert Enum.member?(activities, boost_activity)
  629. assert Enum.member?(activities, activity_one)
  630. end
  631. test "doesn't return thread muted activities" do
  632. user = insert(:user)
  633. _activity_one = insert(:note_activity)
  634. note_two = insert(:note, data: %{"context" => "suya.."})
  635. activity_two = insert(:note_activity, note: note_two)
  636. {:ok, _activity_two} = CommonAPI.add_mute(user, activity_two)
  637. assert [_activity_one] = ActivityPub.fetch_activities([], %{muting_user: user})
  638. end
  639. test "returns thread muted activities when with_muted is set" do
  640. user = insert(:user)
  641. _activity_one = insert(:note_activity)
  642. note_two = insert(:note, data: %{"context" => "suya.."})
  643. activity_two = insert(:note_activity, note: note_two)
  644. {:ok, _activity_two} = CommonAPI.add_mute(user, activity_two)
  645. assert [_activity_two, _activity_one] =
  646. ActivityPub.fetch_activities([], %{muting_user: user, with_muted: true})
  647. end
  648. test "does include announces on request" do
  649. activity_three = insert(:note_activity)
  650. user = insert(:user)
  651. booster = insert(:user)
  652. {:ok, user} = User.follow(user, booster)
  653. {:ok, announce} = CommonAPI.repeat(activity_three.id, booster)
  654. [announce_activity] = ActivityPub.fetch_activities([user.ap_id | User.following(user)])
  655. assert announce_activity.id == announce.id
  656. end
  657. test "excludes reblogs on request" do
  658. user = insert(:user)
  659. {:ok, expected_activity} = ActivityBuilder.insert(%{"type" => "Create"}, %{:user => user})
  660. {:ok, _} = ActivityBuilder.insert(%{"type" => "Announce"}, %{:user => user})
  661. [activity] = ActivityPub.fetch_user_activities(user, nil, %{exclude_reblogs: true})
  662. assert activity == expected_activity
  663. end
  664. describe "irreversible filters" do
  665. setup do
  666. user = insert(:user)
  667. user_two = insert(:user)
  668. insert(:filter, user: user_two, phrase: "cofe", hide: true)
  669. insert(:filter, user: user_two, phrase: "ok boomer", hide: true)
  670. insert(:filter, user: user_two, phrase: "test", hide: false)
  671. params = %{
  672. type: ["Create", "Announce"],
  673. user: user_two
  674. }
  675. {:ok, %{user: user, user_two: user_two, params: params}}
  676. end
  677. test "it returns statuses if they don't contain exact filter words", %{
  678. user: user,
  679. params: params
  680. } do
  681. {:ok, _} = CommonAPI.post(user, %{status: "hey"})
  682. {:ok, _} = CommonAPI.post(user, %{status: "got cofefe?"})
  683. {:ok, _} = CommonAPI.post(user, %{status: "I am not a boomer"})
  684. {:ok, _} = CommonAPI.post(user, %{status: "ok boomers"})
  685. {:ok, _} = CommonAPI.post(user, %{status: "ccofee is not a word"})
  686. {:ok, _} = CommonAPI.post(user, %{status: "this is a test"})
  687. activities = ActivityPub.fetch_activities([], params)
  688. assert Enum.count(activities) == 6
  689. end
  690. test "it does not filter user's own statuses", %{user_two: user_two, params: params} do
  691. {:ok, _} = CommonAPI.post(user_two, %{status: "Give me some cofe!"})
  692. {:ok, _} = CommonAPI.post(user_two, %{status: "ok boomer"})
  693. activities = ActivityPub.fetch_activities([], params)
  694. assert Enum.count(activities) == 2
  695. end
  696. test "it excludes statuses with filter words", %{user: user, params: params} do
  697. {:ok, _} = CommonAPI.post(user, %{status: "Give me some cofe!"})
  698. {:ok, _} = CommonAPI.post(user, %{status: "ok boomer"})
  699. {:ok, _} = CommonAPI.post(user, %{status: "is it a cOfE?"})
  700. {:ok, _} = CommonAPI.post(user, %{status: "cofe is all I need"})
  701. {:ok, _} = CommonAPI.post(user, %{status: "— ok BOOMER\n"})
  702. activities = ActivityPub.fetch_activities([], params)
  703. assert Enum.empty?(activities)
  704. end
  705. test "it returns all statuses if user does not have any filters" do
  706. another_user = insert(:user)
  707. {:ok, _} = CommonAPI.post(another_user, %{status: "got cofe?"})
  708. {:ok, _} = CommonAPI.post(another_user, %{status: "test!"})
  709. activities =
  710. ActivityPub.fetch_activities([], %{
  711. type: ["Create", "Announce"],
  712. user: another_user
  713. })
  714. assert Enum.count(activities) == 2
  715. end
  716. end
  717. describe "public fetch activities" do
  718. test "doesn't retrieve unlisted activities" do
  719. user = insert(:user)
  720. {:ok, _unlisted_activity} = CommonAPI.post(user, %{status: "yeah", visibility: "unlisted"})
  721. {:ok, listed_activity} = CommonAPI.post(user, %{status: "yeah"})
  722. [activity] = ActivityPub.fetch_public_activities()
  723. assert activity == listed_activity
  724. end
  725. test "retrieves public activities" do
  726. _activities = ActivityPub.fetch_public_activities()
  727. %{public: public} = ActivityBuilder.public_and_non_public()
  728. activities = ActivityPub.fetch_public_activities()
  729. assert length(activities) == 1
  730. assert Enum.at(activities, 0) == public
  731. end
  732. test "retrieves a maximum of 20 activities" do
  733. ActivityBuilder.insert_list(10)
  734. expected_activities = ActivityBuilder.insert_list(20)
  735. activities = ActivityPub.fetch_public_activities()
  736. assert collect_ids(activities) == collect_ids(expected_activities)
  737. assert length(activities) == 20
  738. end
  739. test "retrieves ids starting from a since_id" do
  740. activities = ActivityBuilder.insert_list(30)
  741. expected_activities = ActivityBuilder.insert_list(10)
  742. since_id = List.last(activities).id
  743. activities = ActivityPub.fetch_public_activities(%{since_id: since_id})
  744. assert collect_ids(activities) == collect_ids(expected_activities)
  745. assert length(activities) == 10
  746. end
  747. test "retrieves ids up to max_id" do
  748. ActivityBuilder.insert_list(10)
  749. expected_activities = ActivityBuilder.insert_list(20)
  750. %{id: max_id} =
  751. 10
  752. |> ActivityBuilder.insert_list()
  753. |> List.first()
  754. activities = ActivityPub.fetch_public_activities(%{max_id: max_id})
  755. assert length(activities) == 20
  756. assert collect_ids(activities) == collect_ids(expected_activities)
  757. end
  758. test "paginates via offset/limit" do
  759. _first_part_activities = ActivityBuilder.insert_list(10)
  760. second_part_activities = ActivityBuilder.insert_list(10)
  761. later_activities = ActivityBuilder.insert_list(10)
  762. activities = ActivityPub.fetch_public_activities(%{page: "2", page_size: "20"}, :offset)
  763. assert length(activities) == 20
  764. assert collect_ids(activities) ==
  765. collect_ids(second_part_activities) ++ collect_ids(later_activities)
  766. end
  767. test "doesn't return reblogs for users for whom reblogs have been muted" do
  768. activity = insert(:note_activity)
  769. user = insert(:user)
  770. booster = insert(:user)
  771. {:ok, _reblog_mute} = CommonAPI.hide_reblogs(user, booster)
  772. {:ok, activity} = CommonAPI.repeat(activity.id, booster)
  773. activities = ActivityPub.fetch_activities([], %{muting_user: user})
  774. refute Enum.any?(activities, fn %{id: id} -> id == activity.id end)
  775. end
  776. test "returns reblogs for users for whom reblogs have not been muted" do
  777. activity = insert(:note_activity)
  778. user = insert(:user)
  779. booster = insert(:user)
  780. {:ok, _reblog_mute} = CommonAPI.hide_reblogs(user, booster)
  781. {:ok, _reblog_mute} = CommonAPI.show_reblogs(user, booster)
  782. {:ok, activity} = CommonAPI.repeat(activity.id, booster)
  783. activities = ActivityPub.fetch_activities([], %{muting_user: user})
  784. assert Enum.any?(activities, fn %{id: id} -> id == activity.id end)
  785. end
  786. end
  787. describe "uploading files" do
  788. setup do
  789. test_file = %Plug.Upload{
  790. content_type: "image/jpg",
  791. path: Path.absname("test/fixtures/image.jpg"),
  792. filename: "an_image.jpg"
  793. }
  794. %{test_file: test_file}
  795. end
  796. test "sets a description if given", %{test_file: file} do
  797. {:ok, %Object{} = object} = ActivityPub.upload(file, description: "a cool file")
  798. assert object.data["name"] == "a cool file"
  799. end
  800. test "it sets the default description depending on the configuration", %{test_file: file} do
  801. clear_config([Pleroma.Upload, :default_description])
  802. Pleroma.Config.put([Pleroma.Upload, :default_description], nil)
  803. {:ok, %Object{} = object} = ActivityPub.upload(file)
  804. assert object.data["name"] == ""
  805. Pleroma.Config.put([Pleroma.Upload, :default_description], :filename)
  806. {:ok, %Object{} = object} = ActivityPub.upload(file)
  807. assert object.data["name"] == "an_image.jpg"
  808. Pleroma.Config.put([Pleroma.Upload, :default_description], "unnamed attachment")
  809. {:ok, %Object{} = object} = ActivityPub.upload(file)
  810. assert object.data["name"] == "unnamed attachment"
  811. end
  812. test "copies the file to the configured folder", %{test_file: file} do
  813. clear_config([Pleroma.Upload, :default_description], :filename)
  814. {:ok, %Object{} = object} = ActivityPub.upload(file)
  815. assert object.data["name"] == "an_image.jpg"
  816. end
  817. test "works with base64 encoded images" do
  818. file = %{
  819. img: data_uri()
  820. }
  821. {:ok, %Object{}} = ActivityPub.upload(file)
  822. end
  823. end
  824. describe "fetch the latest Follow" do
  825. test "fetches the latest Follow activity" do
  826. %Activity{data: %{"type" => "Follow"}} = activity = insert(:follow_activity)
  827. follower = Repo.get_by(User, ap_id: activity.data["actor"])
  828. followed = Repo.get_by(User, ap_id: activity.data["object"])
  829. assert activity == Utils.fetch_latest_follow(follower, followed)
  830. end
  831. end
  832. describe "unfollowing" do
  833. test "it reverts unfollow activity" do
  834. follower = insert(:user)
  835. followed = insert(:user)
  836. {:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed)
  837. with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
  838. assert {:error, :reverted} = ActivityPub.unfollow(follower, followed)
  839. end
  840. activity = Activity.get_by_id(follow_activity.id)
  841. assert activity.data["type"] == "Follow"
  842. assert activity.data["actor"] == follower.ap_id
  843. assert activity.data["object"] == followed.ap_id
  844. end
  845. test "creates an undo activity for the last follow" do
  846. follower = insert(:user)
  847. followed = insert(:user)
  848. {:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed)
  849. {:ok, activity} = ActivityPub.unfollow(follower, followed)
  850. assert activity.data["type"] == "Undo"
  851. assert activity.data["actor"] == follower.ap_id
  852. embedded_object = activity.data["object"]
  853. assert is_map(embedded_object)
  854. assert embedded_object["type"] == "Follow"
  855. assert embedded_object["object"] == followed.ap_id
  856. assert embedded_object["id"] == follow_activity.data["id"]
  857. end
  858. test "creates an undo activity for a pending follow request" do
  859. follower = insert(:user)
  860. followed = insert(:user, %{locked: true})
  861. {:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed)
  862. {:ok, activity} = ActivityPub.unfollow(follower, followed)
  863. assert activity.data["type"] == "Undo"
  864. assert activity.data["actor"] == follower.ap_id
  865. embedded_object = activity.data["object"]
  866. assert is_map(embedded_object)
  867. assert embedded_object["type"] == "Follow"
  868. assert embedded_object["object"] == followed.ap_id
  869. assert embedded_object["id"] == follow_activity.data["id"]
  870. end
  871. end
  872. describe "timeline post-processing" do
  873. test "it filters broken threads" do
  874. user1 = insert(:user)
  875. user2 = insert(:user)
  876. user3 = insert(:user)
  877. {:ok, user1} = User.follow(user1, user3)
  878. assert User.following?(user1, user3)
  879. {:ok, user2} = User.follow(user2, user3)
  880. assert User.following?(user2, user3)
  881. {:ok, user3} = User.follow(user3, user2)
  882. assert User.following?(user3, user2)
  883. {:ok, public_activity} = CommonAPI.post(user3, %{status: "hi 1"})
  884. {:ok, private_activity_1} = CommonAPI.post(user3, %{status: "hi 2", visibility: "private"})
  885. {:ok, private_activity_2} =
  886. CommonAPI.post(user2, %{
  887. status: "hi 3",
  888. visibility: "private",
  889. in_reply_to_status_id: private_activity_1.id
  890. })
  891. {:ok, private_activity_3} =
  892. CommonAPI.post(user3, %{
  893. status: "hi 4",
  894. visibility: "private",
  895. in_reply_to_status_id: private_activity_2.id
  896. })
  897. activities =
  898. ActivityPub.fetch_activities([user1.ap_id | User.following(user1)])
  899. |> Enum.map(fn a -> a.id end)
  900. private_activity_1 = Activity.get_by_ap_id_with_object(private_activity_1.data["id"])
  901. assert [public_activity.id, private_activity_1.id, private_activity_3.id] == activities
  902. assert length(activities) == 3
  903. activities =
  904. ActivityPub.fetch_activities([user1.ap_id | User.following(user1)], %{user: user1})
  905. |> Enum.map(fn a -> a.id end)
  906. assert [public_activity.id, private_activity_1.id] == activities
  907. assert length(activities) == 2
  908. end
  909. end
  910. describe "flag/1" do
  911. setup do
  912. reporter = insert(:user)
  913. target_account = insert(:user)
  914. content = "foobar"
  915. {:ok, activity} = CommonAPI.post(target_account, %{status: content})
  916. context = Utils.generate_context_id()
  917. reporter_ap_id = reporter.ap_id
  918. target_ap_id = target_account.ap_id
  919. activity_ap_id = activity.data["id"]
  920. activity_with_object = Activity.get_by_ap_id_with_object(activity_ap_id)
  921. {:ok,
  922. %{
  923. reporter: reporter,
  924. context: context,
  925. target_account: target_account,
  926. reported_activity: activity,
  927. content: content,
  928. activity_ap_id: activity_ap_id,
  929. activity_with_object: activity_with_object,
  930. reporter_ap_id: reporter_ap_id,
  931. target_ap_id: target_ap_id
  932. }}
  933. end
  934. test "it can create a Flag activity",
  935. %{
  936. reporter: reporter,
  937. context: context,
  938. target_account: target_account,
  939. reported_activity: reported_activity,
  940. content: content,
  941. activity_ap_id: activity_ap_id,
  942. activity_with_object: activity_with_object,
  943. reporter_ap_id: reporter_ap_id,
  944. target_ap_id: target_ap_id
  945. } do
  946. assert {:ok, activity} =
  947. ActivityPub.flag(%{
  948. actor: reporter,
  949. context: context,
  950. account: target_account,
  951. statuses: [reported_activity],
  952. content: content
  953. })
  954. note_obj = %{
  955. "type" => "Note",
  956. "id" => activity_ap_id,
  957. "content" => content,
  958. "published" => activity_with_object.object.data["published"],
  959. "actor" =>
  960. AccountView.render("show.json", %{user: target_account, skip_visibility_check: true})
  961. }
  962. assert %Activity{
  963. actor: ^reporter_ap_id,
  964. data: %{
  965. "type" => "Flag",
  966. "content" => ^content,
  967. "context" => ^context,
  968. "object" => [^target_ap_id, ^note_obj]
  969. }
  970. } = activity
  971. end
  972. test_with_mock "strips status data from Flag, before federating it",
  973. %{
  974. reporter: reporter,
  975. context: context,
  976. target_account: target_account,
  977. reported_activity: reported_activity,
  978. content: content
  979. },
  980. Utils,
  981. [:passthrough],
  982. [] do
  983. {:ok, activity} =
  984. ActivityPub.flag(%{
  985. actor: reporter,
  986. context: context,
  987. account: target_account,
  988. statuses: [reported_activity],
  989. content: content
  990. })
  991. new_data =
  992. put_in(activity.data, ["object"], [target_account.ap_id, reported_activity.data["id"]])
  993. assert_called(Utils.maybe_federate(%{activity | data: new_data}))
  994. end
  995. end
  996. test "fetch_activities/2 returns activities addressed to a list " do
  997. user = insert(:user)
  998. member = insert(:user)
  999. {:ok, list} = Pleroma.List.create("foo", user)
  1000. {:ok, list} = Pleroma.List.follow(list, member)
  1001. {:ok, activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"})
  1002. activity = Repo.preload(activity, :bookmark)
  1003. activity = %Activity{activity | thread_muted?: !!activity.thread_muted?}
  1004. assert ActivityPub.fetch_activities([], %{user: user}) == [activity]
  1005. end
  1006. def data_uri do
  1007. File.read!("test/fixtures/avatar_data_uri")
  1008. end
  1009. describe "fetch_activities_bounded" do
  1010. test "fetches private posts for followed users" do
  1011. user = insert(:user)
  1012. {:ok, activity} =
  1013. CommonAPI.post(user, %{
  1014. status: "thought I looked cute might delete later :3",
  1015. visibility: "private"
  1016. })
  1017. [result] = ActivityPub.fetch_activities_bounded([user.follower_address], [])
  1018. assert result.id == activity.id
  1019. end
  1020. test "fetches only public posts for other users" do
  1021. user = insert(:user)
  1022. {:ok, activity} = CommonAPI.post(user, %{status: "#cofe", visibility: "public"})
  1023. {:ok, _private_activity} =
  1024. CommonAPI.post(user, %{
  1025. status: "why is tenshi eating a corndog so cute?",
  1026. visibility: "private"
  1027. })
  1028. [result] = ActivityPub.fetch_activities_bounded([], [user.follower_address])
  1029. assert result.id == activity.id
  1030. end
  1031. end
  1032. describe "fetch_follow_information_for_user" do
  1033. test "syncronizes following/followers counters" do
  1034. user =
  1035. insert(:user,
  1036. local: false,
  1037. follower_address: "http://localhost:4001/users/fuser2/followers",
  1038. following_address: "http://localhost:4001/users/fuser2/following"
  1039. )
  1040. {:ok, info} = ActivityPub.fetch_follow_information_for_user(user)
  1041. assert info.follower_count == 527
  1042. assert info.following_count == 267
  1043. end
  1044. test "detects hidden followers" do
  1045. mock(fn env ->
  1046. case env.url do
  1047. "http://localhost:4001/users/masto_closed/followers?page=1" ->
  1048. %Tesla.Env{status: 403, body: ""}
  1049. _ ->
  1050. apply(HttpRequestMock, :request, [env])
  1051. end
  1052. end)
  1053. user =
  1054. insert(:user,
  1055. local: false,
  1056. follower_address: "http://localhost:4001/users/masto_closed/followers",
  1057. following_address: "http://localhost:4001/users/masto_closed/following"
  1058. )
  1059. {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
  1060. assert follow_info.hide_followers == true
  1061. assert follow_info.hide_follows == false
  1062. end
  1063. test "detects hidden follows" do
  1064. mock(fn env ->
  1065. case env.url do
  1066. "http://localhost:4001/users/masto_closed/following?page=1" ->
  1067. %Tesla.Env{status: 403, body: ""}
  1068. _ ->
  1069. apply(HttpRequestMock, :request, [env])
  1070. end
  1071. end)
  1072. user =
  1073. insert(:user,
  1074. local: false,
  1075. follower_address: "http://localhost:4001/users/masto_closed/followers",
  1076. following_address: "http://localhost:4001/users/masto_closed/following"
  1077. )
  1078. {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
  1079. assert follow_info.hide_followers == false
  1080. assert follow_info.hide_follows == true
  1081. end
  1082. test "detects hidden follows/followers for friendica" do
  1083. user =
  1084. insert(:user,
  1085. local: false,
  1086. follower_address: "http://localhost:8080/followers/fuser3",
  1087. following_address: "http://localhost:8080/following/fuser3"
  1088. )
  1089. {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
  1090. assert follow_info.hide_followers == true
  1091. assert follow_info.follower_count == 296
  1092. assert follow_info.following_count == 32
  1093. assert follow_info.hide_follows == true
  1094. end
  1095. test "doesn't crash when follower and following counters are hidden" do
  1096. mock(fn env ->
  1097. case env.url do
  1098. "http://localhost:4001/users/masto_hidden_counters/following" ->
  1099. json(%{
  1100. "@context" => "https://www.w3.org/ns/activitystreams",
  1101. "id" => "http://localhost:4001/users/masto_hidden_counters/followers"
  1102. })
  1103. "http://localhost:4001/users/masto_hidden_counters/following?page=1" ->
  1104. %Tesla.Env{status: 403, body: ""}
  1105. "http://localhost:4001/users/masto_hidden_counters/followers" ->
  1106. json(%{
  1107. "@context" => "https://www.w3.org/ns/activitystreams",
  1108. "id" => "http://localhost:4001/users/masto_hidden_counters/following"
  1109. })
  1110. "http://localhost:4001/users/masto_hidden_counters/followers?page=1" ->
  1111. %Tesla.Env{status: 403, body: ""}
  1112. end
  1113. end)
  1114. user =
  1115. insert(:user,
  1116. local: false,
  1117. follower_address: "http://localhost:4001/users/masto_hidden_counters/followers",
  1118. following_address: "http://localhost:4001/users/masto_hidden_counters/following"
  1119. )
  1120. {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
  1121. assert follow_info.hide_followers == true
  1122. assert follow_info.follower_count == 0
  1123. assert follow_info.hide_follows == true
  1124. assert follow_info.following_count == 0
  1125. end
  1126. end
  1127. describe "fetch_favourites/3" do
  1128. test "returns a favourite activities sorted by adds to favorite" do
  1129. user = insert(:user)
  1130. other_user = insert(:user)
  1131. user1 = insert(:user)
  1132. user2 = insert(:user)
  1133. {:ok, a1} = CommonAPI.post(user1, %{status: "bla"})
  1134. {:ok, _a2} = CommonAPI.post(user2, %{status: "traps are happy"})
  1135. {:ok, a3} = CommonAPI.post(user2, %{status: "Trees Are "})
  1136. {:ok, a4} = CommonAPI.post(user2, %{status: "Agent Smith "})
  1137. {:ok, a5} = CommonAPI.post(user1, %{status: "Red or Blue "})
  1138. {:ok, _} = CommonAPI.favorite(user, a4.id)
  1139. {:ok, _} = CommonAPI.favorite(other_user, a3.id)
  1140. {:ok, _} = CommonAPI.favorite(user, a3.id)
  1141. {:ok, _} = CommonAPI.favorite(other_user, a5.id)
  1142. {:ok, _} = CommonAPI.favorite(user, a5.id)
  1143. {:ok, _} = CommonAPI.favorite(other_user, a4.id)
  1144. {:ok, _} = CommonAPI.favorite(user, a1.id)
  1145. {:ok, _} = CommonAPI.favorite(other_user, a1.id)
  1146. result = ActivityPub.fetch_favourites(user)
  1147. assert Enum.map(result, & &1.id) == [a1.id, a5.id, a3.id, a4.id]
  1148. result = ActivityPub.fetch_favourites(user, %{limit: 2})
  1149. assert Enum.map(result, & &1.id) == [a1.id, a5.id]
  1150. end
  1151. end
  1152. describe "Move activity" do
  1153. test "create" do
  1154. %{ap_id: old_ap_id} = old_user = insert(:user)
  1155. %{ap_id: new_ap_id} = new_user = insert(:user, also_known_as: [old_ap_id])
  1156. follower = insert(:user)
  1157. follower_move_opted_out = insert(:user, allow_following_move: false)
  1158. User.follow(follower, old_user)
  1159. User.follow(follower_move_opted_out, old_user)
  1160. assert User.following?(follower, old_user)
  1161. assert User.following?(follower_move_opted_out, old_user)
  1162. assert {:ok, activity} = ActivityPub.move(old_user, new_user)
  1163. assert %Activity{
  1164. actor: ^old_ap_id,
  1165. data: %{
  1166. "actor" => ^old_ap_id,
  1167. "object" => ^old_ap_id,
  1168. "target" => ^new_ap_id,
  1169. "type" => "Move"
  1170. },
  1171. local: true
  1172. } = activity
  1173. params = %{
  1174. "op" => "move_following",
  1175. "origin_id" => old_user.id,
  1176. "target_id" => new_user.id
  1177. }
  1178. assert_enqueued(worker: Pleroma.Workers.BackgroundWorker, args: params)
  1179. Pleroma.Workers.BackgroundWorker.perform(%Oban.Job{args: params})
  1180. refute User.following?(follower, old_user)
  1181. assert User.following?(follower, new_user)
  1182. assert User.following?(follower_move_opted_out, old_user)
  1183. refute User.following?(follower_move_opted_out, new_user)
  1184. activity = %Activity{activity | object: nil}
  1185. assert [%Notification{activity: ^activity}] = Notification.for_user(follower)
  1186. assert [%Notification{activity: ^activity}] = Notification.for_user(follower_move_opted_out)
  1187. end
  1188. test "old user must be in the new user's `also_known_as` list" do
  1189. old_user = insert(:user)
  1190. new_user = insert(:user)
  1191. assert {:error, "Target account must have the origin in `alsoKnownAs`"} =
  1192. ActivityPub.move(old_user, new_user)
  1193. end
  1194. end
  1195. test "doesn't retrieve replies activities with exclude_replies" do
  1196. user = insert(:user)
  1197. {:ok, activity} = CommonAPI.post(user, %{status: "yeah"})
  1198. {:ok, _reply} = CommonAPI.post(user, %{status: "yeah", in_reply_to_status_id: activity.id})
  1199. [result] = ActivityPub.fetch_public_activities(%{exclude_replies: true})
  1200. assert result.id == activity.id
  1201. assert length(ActivityPub.fetch_public_activities()) == 2
  1202. end
  1203. describe "replies filtering with public messages" do
  1204. setup :public_messages
  1205. test "public timeline", %{users: %{u1: user}} do
  1206. activities_ids =
  1207. %{}
  1208. |> Map.put(:type, ["Create", "Announce"])
  1209. |> Map.put(:local_only, false)
  1210. |> Map.put(:blocking_user, user)
  1211. |> Map.put(:muting_user, user)
  1212. |> Map.put(:reply_filtering_user, user)
  1213. |> ActivityPub.fetch_public_activities()
  1214. |> Enum.map(& &1.id)
  1215. assert length(activities_ids) == 16
  1216. end
  1217. test "public timeline with reply_visibility `following`", %{
  1218. users: %{u1: user},
  1219. u1: u1,
  1220. u2: u2,
  1221. u3: u3,
  1222. u4: u4,
  1223. activities: activities
  1224. } do
  1225. activities_ids =
  1226. %{}
  1227. |> Map.put(:type, ["Create", "Announce"])
  1228. |> Map.put(:local_only, false)
  1229. |> Map.put(:blocking_user, user)
  1230. |> Map.put(:muting_user, user)
  1231. |> Map.put(:reply_visibility, "following")
  1232. |> Map.put(:reply_filtering_user, user)
  1233. |> ActivityPub.fetch_public_activities()
  1234. |> Enum.map(& &1.id)
  1235. assert length(activities_ids) == 14
  1236. visible_ids =
  1237. Map.values(u1) ++ Map.values(u2) ++ Map.values(u4) ++ Map.values(activities) ++ [u3[:r1]]
  1238. assert Enum.all?(visible_ids, &(&1 in activities_ids))
  1239. end
  1240. test "public timeline with reply_visibility `self`", %{
  1241. users: %{u1: user},
  1242. u1: u1,
  1243. u2: u2,
  1244. u3: u3,
  1245. u4: u4,
  1246. activities: activities
  1247. } do
  1248. activities_ids =
  1249. %{}
  1250. |> Map.put(:type, ["Create", "Announce"])
  1251. |> Map.put(:local_only, false)
  1252. |> Map.put(:blocking_user, user)
  1253. |> Map.put(:muting_user, user)
  1254. |> Map.put(:reply_visibility, "self")
  1255. |> Map.put(:reply_filtering_user, user)
  1256. |> ActivityPub.fetch_public_activities()
  1257. |> Enum.map(& &1.id)
  1258. assert length(activities_ids) == 10
  1259. visible_ids = Map.values(u1) ++ [u2[:r1], u3[:r1], u4[:r1]] ++ Map.values(activities)
  1260. assert Enum.all?(visible_ids, &(&1 in activities_ids))
  1261. end
  1262. test "home timeline", %{
  1263. users: %{u1: user},
  1264. activities: activities,
  1265. u1: u1,
  1266. u2: u2,
  1267. u3: u3,
  1268. u4: u4
  1269. } do
  1270. params =
  1271. %{}
  1272. |> Map.put(:type, ["Create", "Announce"])
  1273. |> Map.put(:blocking_user, user)
  1274. |> Map.put(:muting_user, user)
  1275. |> Map.put(:user, user)
  1276. |> Map.put(:reply_filtering_user, user)
  1277. activities_ids =
  1278. ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
  1279. |> Enum.map(& &1.id)
  1280. assert length(activities_ids) == 13
  1281. visible_ids =
  1282. Map.values(u1) ++
  1283. Map.values(u3) ++
  1284. [
  1285. activities[:a1],
  1286. activities[:a2],
  1287. activities[:a4],
  1288. u2[:r1],
  1289. u2[:r3],
  1290. u4[:r1],
  1291. u4[:r2]
  1292. ]
  1293. assert Enum.all?(visible_ids, &(&1 in activities_ids))
  1294. end
  1295. test "home timeline with reply_visibility `following`", %{
  1296. users: %{u1: user},
  1297. activities: activities,
  1298. u1: u1,
  1299. u2: u2,
  1300. u3: u3,
  1301. u4: u4
  1302. } do
  1303. params =
  1304. %{}
  1305. |> Map.put(:type, ["Create", "Announce"])
  1306. |> Map.put(:blocking_user, user)
  1307. |> Map.put(:muting_user, user)
  1308. |> Map.put(:user, user)
  1309. |> Map.put(:reply_visibility, "following")
  1310. |> Map.put(:reply_filtering_user, user)
  1311. activities_ids =
  1312. ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
  1313. |> Enum.map(& &1.id)
  1314. assert length(activities_ids) == 11
  1315. visible_ids =
  1316. Map.values(u1) ++
  1317. [
  1318. activities[:a1],
  1319. activities[:a2],
  1320. activities[:a4],
  1321. u2[:r1],
  1322. u2[:r3],
  1323. u3[:r1],
  1324. u4[:r1],
  1325. u4[:r2]
  1326. ]
  1327. assert Enum.all?(visible_ids, &(&1 in activities_ids))
  1328. end
  1329. test "home timeline with reply_visibility `self`", %{
  1330. users: %{u1: user},
  1331. activities: activities,
  1332. u1: u1,
  1333. u2: u2,
  1334. u3: u3,
  1335. u4: u4
  1336. } do
  1337. params =
  1338. %{}
  1339. |> Map.put(:type, ["Create", "Announce"])
  1340. |> Map.put(:blocking_user, user)
  1341. |> Map.put(:muting_user, user)
  1342. |> Map.put(:user, user)
  1343. |> Map.put(:reply_visibility, "self")
  1344. |> Map.put(:reply_filtering_user, user)
  1345. activities_ids =
  1346. ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
  1347. |> Enum.map(& &1.id)
  1348. assert length(activities_ids) == 9
  1349. visible_ids =
  1350. Map.values(u1) ++
  1351. [
  1352. activities[:a1],
  1353. activities[:a2],
  1354. activities[:a4],
  1355. u2[:r1],
  1356. u3[:r1],
  1357. u4[:r1]
  1358. ]
  1359. assert Enum.all?(visible_ids, &(&1 in activities_ids))
  1360. end
  1361. test "filtering out announces where the user is the actor of the announced message" do
  1362. user = insert(:user)
  1363. other_user = insert(:user)
  1364. third_user = insert(:user)
  1365. User.follow(user, other_user)
  1366. {:ok, post} = CommonAPI.post(user, %{status: "yo"})
  1367. {:ok, other_post} = CommonAPI.post(third_user, %{status: "yo"})
  1368. {:ok, _announce} = CommonAPI.repeat(post.id, other_user)
  1369. {:ok, _announce} = CommonAPI.repeat(post.id, third_user)
  1370. {:ok, announce} = CommonAPI.repeat(other_post.id, other_user)
  1371. params = %{
  1372. type: ["Announce"]
  1373. }
  1374. results =
  1375. [user.ap_id | User.following(user)]
  1376. |> ActivityPub.fetch_activities(params)
  1377. assert length(results) == 3
  1378. params = %{
  1379. type: ["Announce"],
  1380. announce_filtering_user: user
  1381. }
  1382. [result] =
  1383. [user.ap_id | User.following(user)]
  1384. |> ActivityPub.fetch_activities(params)
  1385. assert result.id == announce.id
  1386. end
  1387. end
  1388. describe "replies filtering with private messages" do
  1389. setup :private_messages
  1390. test "public timeline", %{users: %{u1: user}} do
  1391. activities_ids =
  1392. %{}
  1393. |> Map.put(:type, ["Create", "Announce"])
  1394. |> Map.put(:local_only, false)
  1395. |> Map.put(:blocking_user, user)
  1396. |> Map.put(:muting_user, user)
  1397. |> Map.put(:user, user)
  1398. |> ActivityPub.fetch_public_activities()
  1399. |> Enum.map(& &1.id)
  1400. assert activities_ids == []
  1401. end
  1402. test "public timeline with default reply_visibility `following`", %{users: %{u1: user}} do
  1403. activities_ids =
  1404. %{}
  1405. |> Map.put(:type, ["Create", "Announce"])
  1406. |> Map.put(:local_only, false)
  1407. |> Map.put(:blocking_user, user)
  1408. |> Map.put(:muting_user, user)
  1409. |> Map.put(:reply_visibility, "following")
  1410. |> Map.put(:reply_filtering_user, user)
  1411. |> Map.put(:user, user)
  1412. |> ActivityPub.fetch_public_activities()
  1413. |> Enum.map(& &1.id)
  1414. assert activities_ids == []
  1415. end
  1416. test "public timeline with default reply_visibility `self`", %{users: %{u1: user}} do
  1417. activities_ids =
  1418. %{}
  1419. |> Map.put(:type, ["Create", "Announce"])
  1420. |> Map.put(:local_only, false)
  1421. |> Map.put(:blocking_user, user)
  1422. |> Map.put(:muting_user, user)
  1423. |> Map.put(:reply_visibility, "self")
  1424. |> Map.put(:reply_filtering_user, user)
  1425. |> Map.put(:user, user)
  1426. |> ActivityPub.fetch_public_activities()
  1427. |> Enum.map(& &1.id)
  1428. assert activities_ids == []
  1429. activities_ids =
  1430. %{}
  1431. |> Map.put(:reply_visibility, "self")
  1432. |> Map.put(:reply_filtering_user, nil)
  1433. |> ActivityPub.fetch_public_activities()
  1434. assert activities_ids == []
  1435. end
  1436. test "home timeline", %{users: %{u1: user}} do
  1437. params =
  1438. %{}
  1439. |> Map.put(:type, ["Create", "Announce"])
  1440. |> Map.put(:blocking_user, user)
  1441. |> Map.put(:muting_user, user)
  1442. |> Map.put(:user, user)
  1443. activities_ids =
  1444. ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
  1445. |> Enum.map(& &1.id)
  1446. assert length(activities_ids) == 12
  1447. end
  1448. test "home timeline with default reply_visibility `following`", %{users: %{u1: user}} do
  1449. params =
  1450. %{}
  1451. |> Map.put(:type, ["Create", "Announce"])
  1452. |> Map.put(:blocking_user, user)
  1453. |> Map.put(:muting_user, user)
  1454. |> Map.put(:user, user)
  1455. |> Map.put(:reply_visibility, "following")
  1456. |> Map.put(:reply_filtering_user, user)
  1457. activities_ids =
  1458. ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
  1459. |> Enum.map(& &1.id)
  1460. assert length(activities_ids) == 12
  1461. end
  1462. test "home timeline with default reply_visibility `self`", %{
  1463. users: %{u1: user},
  1464. activities: activities,
  1465. u1: u1,
  1466. u2: u2,
  1467. u3: u3,
  1468. u4: u4
  1469. } do
  1470. params =
  1471. %{}
  1472. |> Map.put(:type, ["Create", "Announce"])
  1473. |> Map.put(:blocking_user, user)
  1474. |> Map.put(:muting_user, user)
  1475. |> Map.put(:user, user)
  1476. |> Map.put(:reply_visibility, "self")
  1477. |> Map.put(:reply_filtering_user, user)
  1478. activities_ids =
  1479. ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
  1480. |> Enum.map(& &1.id)
  1481. assert length(activities_ids) == 10
  1482. visible_ids =
  1483. Map.values(u1) ++ Map.values(u4) ++ [u2[:r1], u3[:r1]] ++ Map.values(activities)
  1484. assert Enum.all?(visible_ids, &(&1 in activities_ids))
  1485. end
  1486. end
  1487. defp public_messages(_) do
  1488. [u1, u2, u3, u4] = insert_list(4, :user)
  1489. {:ok, u1} = User.follow(u1, u2)
  1490. {:ok, u2} = User.follow(u2, u1)
  1491. {:ok, u1} = User.follow(u1, u4)
  1492. {:ok, u4} = User.follow(u4, u1)
  1493. {:ok, u2} = User.follow(u2, u3)
  1494. {:ok, u3} = User.follow(u3, u2)
  1495. {:ok, a1} = CommonAPI.post(u1, %{status: "Status"})
  1496. {:ok, r1_1} =
  1497. CommonAPI.post(u2, %{
  1498. status: "@#{u1.nickname} reply from u2 to u1",
  1499. in_reply_to_status_id: a1.id
  1500. })
  1501. {:ok, r1_2} =
  1502. CommonAPI.post(u3, %{
  1503. status: "@#{u1.nickname} reply from u3 to u1",
  1504. in_reply_to_status_id: a1.id
  1505. })
  1506. {:ok, r1_3} =
  1507. CommonAPI.post(u4, %{
  1508. status: "@#{u1.nickname} reply from u4 to u1",
  1509. in_reply_to_status_id: a1.id
  1510. })
  1511. {:ok, a2} = CommonAPI.post(u2, %{status: "Status"})
  1512. {:ok, r2_1} =
  1513. CommonAPI.post(u1, %{
  1514. status: "@#{u2.nickname} reply from u1 to u2",
  1515. in_reply_to_status_id: a2.id
  1516. })
  1517. {:ok, r2_2} =
  1518. CommonAPI.post(u3, %{
  1519. status: "@#{u2.nickname} reply from u3 to u2",
  1520. in_reply_to_status_id: a2.id
  1521. })
  1522. {:ok, r2_3} =
  1523. CommonAPI.post(u4, %{
  1524. status: "@#{u2.nickname} reply from u4 to u2",
  1525. in_reply_to_status_id: a2.id
  1526. })
  1527. {:ok, a3} = CommonAPI.post(u3, %{status: "Status"})
  1528. {:ok, r3_1} =
  1529. CommonAPI.post(u1, %{
  1530. status: "@#{u3.nickname} reply from u1 to u3",
  1531. in_reply_to_status_id: a3.id
  1532. })
  1533. {:ok, r3_2} =
  1534. CommonAPI.post(u2, %{
  1535. status: "@#{u3.nickname} reply from u2 to u3",
  1536. in_reply_to_status_id: a3.id
  1537. })
  1538. {:ok, r3_3} =
  1539. CommonAPI.post(u4, %{
  1540. status: "@#{u3.nickname} reply from u4 to u3",
  1541. in_reply_to_status_id: a3.id
  1542. })
  1543. {:ok, a4} = CommonAPI.post(u4, %{status: "Status"})
  1544. {:ok, r4_1} =
  1545. CommonAPI.post(u1, %{
  1546. status: "@#{u4.nickname} reply from u1 to u4",
  1547. in_reply_to_status_id: a4.id
  1548. })
  1549. {:ok, r4_2} =
  1550. CommonAPI.post(u2, %{
  1551. status: "@#{u4.nickname} reply from u2 to u4",
  1552. in_reply_to_status_id: a4.id
  1553. })
  1554. {:ok, r4_3} =
  1555. CommonAPI.post(u3, %{
  1556. status: "@#{u4.nickname} reply from u3 to u4",
  1557. in_reply_to_status_id: a4.id
  1558. })
  1559. {:ok,
  1560. users: %{u1: u1, u2: u2, u3: u3, u4: u4},
  1561. activities: %{a1: a1.id, a2: a2.id, a3: a3.id, a4: a4.id},
  1562. u1: %{r1: r1_1.id, r2: r1_2.id, r3: r1_3.id},
  1563. u2: %{r1: r2_1.id, r2: r2_2.id, r3: r2_3.id},
  1564. u3: %{r1: r3_1.id, r2: r3_2.id, r3: r3_3.id},
  1565. u4: %{r1: r4_1.id, r2: r4_2.id, r3: r4_3.id}}
  1566. end
  1567. defp private_messages(_) do
  1568. [u1, u2, u3, u4] = insert_list(4, :user)
  1569. {:ok, u1} = User.follow(u1, u2)
  1570. {:ok, u2} = User.follow(u2, u1)
  1571. {:ok, u1} = User.follow(u1, u3)
  1572. {:ok, u3} = User.follow(u3, u1)
  1573. {:ok, u1} = User.follow(u1, u4)
  1574. {:ok, u4} = User.follow(u4, u1)
  1575. {:ok, u2} = User.follow(u2, u3)
  1576. {:ok, u3} = User.follow(u3, u2)
  1577. {:ok, a1} = CommonAPI.post(u1, %{status: "Status", visibility: "private"})
  1578. {:ok, r1_1} =
  1579. CommonAPI.post(u2, %{
  1580. status: "@#{u1.nickname} reply from u2 to u1",
  1581. in_reply_to_status_id: a1.id,
  1582. visibility: "private"
  1583. })
  1584. {:ok, r1_2} =
  1585. CommonAPI.post(u3, %{
  1586. status: "@#{u1.nickname} reply from u3 to u1",
  1587. in_reply_to_status_id: a1.id,
  1588. visibility: "private"
  1589. })
  1590. {:ok, r1_3} =
  1591. CommonAPI.post(u4, %{
  1592. status: "@#{u1.nickname} reply from u4 to u1",
  1593. in_reply_to_status_id: a1.id,
  1594. visibility: "private"
  1595. })
  1596. {:ok, a2} = CommonAPI.post(u2, %{status: "Status", visibility: "private"})
  1597. {:ok, r2_1} =
  1598. CommonAPI.post(u1, %{
  1599. status: "@#{u2.nickname} reply from u1 to u2",
  1600. in_reply_to_status_id: a2.id,
  1601. visibility: "private"
  1602. })
  1603. {:ok, r2_2} =
  1604. CommonAPI.post(u3, %{
  1605. status: "@#{u2.nickname} reply from u3 to u2",
  1606. in_reply_to_status_id: a2.id,
  1607. visibility: "private"
  1608. })
  1609. {:ok, a3} = CommonAPI.post(u3, %{status: "Status", visibility: "private"})
  1610. {:ok, r3_1} =
  1611. CommonAPI.post(u1, %{
  1612. status: "@#{u3.nickname} reply from u1 to u3",
  1613. in_reply_to_status_id: a3.id,
  1614. visibility: "private"
  1615. })
  1616. {:ok, r3_2} =
  1617. CommonAPI.post(u2, %{
  1618. status: "@#{u3.nickname} reply from u2 to u3",
  1619. in_reply_to_status_id: a3.id,
  1620. visibility: "private"
  1621. })
  1622. {:ok, a4} = CommonAPI.post(u4, %{status: "Status", visibility: "private"})
  1623. {:ok, r4_1} =
  1624. CommonAPI.post(u1, %{
  1625. status: "@#{u4.nickname} reply from u1 to u4",
  1626. in_reply_to_status_id: a4.id,
  1627. visibility: "private"
  1628. })
  1629. {:ok,
  1630. users: %{u1: u1, u2: u2, u3: u3, u4: u4},
  1631. activities: %{a1: a1.id, a2: a2.id, a3: a3.id, a4: a4.id},
  1632. u1: %{r1: r1_1.id, r2: r1_2.id, r3: r1_3.id},
  1633. u2: %{r1: r2_1.id, r2: r2_2.id},
  1634. u3: %{r1: r3_1.id, r2: r3_2.id},
  1635. u4: %{r1: r4_1.id}}
  1636. end
  1637. describe "maybe_update_follow_information/1" do
  1638. setup do
  1639. clear_config([:instance, :external_user_synchronization], true)
  1640. user = %{
  1641. local: false,
  1642. ap_id: "https://gensokyo.2hu/users/raymoo",
  1643. following_address: "https://gensokyo.2hu/users/following",
  1644. follower_address: "https://gensokyo.2hu/users/followers",
  1645. type: "Person"
  1646. }
  1647. %{user: user}
  1648. end
  1649. test "logs an error when it can't fetch the info", %{user: user} do
  1650. assert capture_log(fn ->
  1651. ActivityPub.maybe_update_follow_information(user)
  1652. end) =~ "Follower/Following counter update for #{user.ap_id} failed"
  1653. end
  1654. test "just returns the input if the user type is Application", %{
  1655. user: user
  1656. } do
  1657. user =
  1658. user
  1659. |> Map.put(:type, "Application")
  1660. refute capture_log(fn ->
  1661. assert ^user = ActivityPub.maybe_update_follow_information(user)
  1662. end) =~ "Follower/Following counter update for #{user.ap_id} failed"
  1663. end
  1664. test "it just returns the input if the user has no following/follower addresses", %{
  1665. user: user
  1666. } do
  1667. user =
  1668. user
  1669. |> Map.put(:following_address, nil)
  1670. |> Map.put(:follower_address, nil)
  1671. refute capture_log(fn ->
  1672. assert ^user = ActivityPub.maybe_update_follow_information(user)
  1673. end) =~ "Follower/Following counter update for #{user.ap_id} failed"
  1674. end
  1675. end
  1676. describe "global activity expiration" do
  1677. test "creates an activity expiration for local Create activities" do
  1678. clear_config([:mrf, :policies], Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy)
  1679. {:ok, activity} = ActivityBuilder.insert(%{"type" => "Create", "context" => "3hu"})
  1680. {:ok, follow} = ActivityBuilder.insert(%{"type" => "Follow", "context" => "3hu"})
  1681. assert_enqueued(
  1682. worker: Pleroma.Workers.PurgeExpiredActivity,
  1683. args: %{activity_id: activity.id},
  1684. scheduled_at:
  1685. activity.inserted_at
  1686. |> DateTime.from_naive!("Etc/UTC")
  1687. |> Timex.shift(days: 365)
  1688. )
  1689. refute_enqueued(
  1690. worker: Pleroma.Workers.PurgeExpiredActivity,
  1691. args: %{activity_id: follow.id}
  1692. )
  1693. end
  1694. end
  1695. describe "handling of clashing nicknames" do
  1696. test "renames an existing user with a clashing nickname and a different ap id" do
  1697. orig_user =
  1698. insert(
  1699. :user,
  1700. local: false,
  1701. nickname: "admin@mastodon.example.org",
  1702. ap_id: "http://mastodon.example.org/users/harinezumigari"
  1703. )
  1704. %{
  1705. nickname: orig_user.nickname,
  1706. ap_id: orig_user.ap_id <> "part_2"
  1707. }
  1708. |> ActivityPub.maybe_handle_clashing_nickname()
  1709. user = User.get_by_id(orig_user.id)
  1710. assert user.nickname == "#{orig_user.id}.admin@mastodon.example.org"
  1711. end
  1712. test "does nothing with a clashing nickname and the same ap id" do
  1713. orig_user =
  1714. insert(
  1715. :user,
  1716. local: false,
  1717. nickname: "admin@mastodon.example.org",
  1718. ap_id: "http://mastodon.example.org/users/harinezumigari"
  1719. )
  1720. %{
  1721. nickname: orig_user.nickname,
  1722. ap_id: orig_user.ap_id
  1723. }
  1724. |> ActivityPub.maybe_handle_clashing_nickname()
  1725. user = User.get_by_id(orig_user.id)
  1726. assert user.nickname == orig_user.nickname
  1727. end
  1728. end
  1729. end