logo

pleroma

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

activity_pub_test.exs (89498B)


  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.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.UnstubbedConfigMock, as: ConfigMock
  13. alias Pleroma.User
  14. alias Pleroma.Web.ActivityPub.ActivityPub
  15. alias Pleroma.Web.ActivityPub.Utils
  16. alias Pleroma.Web.AdminAPI.AccountView
  17. alias Pleroma.Web.CommonAPI
  18. import ExUnit.CaptureLog
  19. import Mock
  20. import Mox
  21. import Pleroma.Factory
  22. import Tesla.Mock
  23. setup do
  24. mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
  25. ConfigMock
  26. |> stub_with(Pleroma.Test.StaticConfig)
  27. :ok
  28. end
  29. setup do: clear_config([:instance, :federating])
  30. describe "streaming out participations" do
  31. test "it streams them out" do
  32. user = insert(:user)
  33. {:ok, activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"})
  34. {:ok, conversation} = Pleroma.Conversation.create_or_bump_for(activity)
  35. participations =
  36. conversation.participations
  37. |> Repo.preload(:user)
  38. with_mock Pleroma.Web.Streamer,
  39. stream: fn _, _ -> nil end do
  40. ActivityPub.stream_out_participations(conversation.participations)
  41. assert called(Pleroma.Web.Streamer.stream("participation", participations))
  42. end
  43. end
  44. test "streams them out on activity creation" do
  45. user_one = insert(:user)
  46. user_two = insert(:user)
  47. with_mock Pleroma.Web.Streamer,
  48. stream: fn _, _ -> nil end do
  49. {:ok, activity} =
  50. CommonAPI.post(user_one, %{
  51. status: "@#{user_two.nickname}",
  52. visibility: "direct"
  53. })
  54. conversation =
  55. activity.data["context"]
  56. |> Pleroma.Conversation.get_for_ap_id()
  57. |> Repo.preload(participations: :user)
  58. assert called(Pleroma.Web.Streamer.stream("participation", conversation.participations))
  59. end
  60. end
  61. end
  62. describe "fetching restricted by visibility" do
  63. test "it restricts by the appropriate visibility" do
  64. user = insert(:user)
  65. {:ok, public_activity} = CommonAPI.post(user, %{status: ".", visibility: "public"})
  66. {:ok, direct_activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"})
  67. {:ok, unlisted_activity} = CommonAPI.post(user, %{status: ".", visibility: "unlisted"})
  68. {:ok, private_activity} = CommonAPI.post(user, %{status: ".", visibility: "private"})
  69. activities = ActivityPub.fetch_activities([], %{visibility: "direct", actor_id: user.ap_id})
  70. assert activities == [direct_activity]
  71. activities =
  72. ActivityPub.fetch_activities([], %{visibility: "unlisted", actor_id: user.ap_id})
  73. assert activities == [unlisted_activity]
  74. activities =
  75. ActivityPub.fetch_activities([], %{visibility: "private", actor_id: user.ap_id})
  76. assert activities == [private_activity]
  77. activities = ActivityPub.fetch_activities([], %{visibility: "public", actor_id: user.ap_id})
  78. assert activities == [public_activity]
  79. activities =
  80. ActivityPub.fetch_activities([], %{
  81. visibility: ~w[private public],
  82. actor_id: user.ap_id
  83. })
  84. assert activities == [public_activity, private_activity]
  85. end
  86. end
  87. describe "fetching excluded by visibility" do
  88. test "it excludes by the appropriate visibility" do
  89. user = insert(:user)
  90. {:ok, public_activity} = CommonAPI.post(user, %{status: ".", visibility: "public"})
  91. {:ok, direct_activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"})
  92. {:ok, unlisted_activity} = CommonAPI.post(user, %{status: ".", visibility: "unlisted"})
  93. {:ok, private_activity} = CommonAPI.post(user, %{status: ".", visibility: "private"})
  94. activities =
  95. ActivityPub.fetch_activities([], %{
  96. exclude_visibilities: "direct",
  97. actor_id: user.ap_id
  98. })
  99. assert public_activity in activities
  100. assert unlisted_activity in activities
  101. assert private_activity in activities
  102. refute direct_activity in activities
  103. activities =
  104. ActivityPub.fetch_activities([], %{
  105. exclude_visibilities: "unlisted",
  106. actor_id: user.ap_id
  107. })
  108. assert public_activity in activities
  109. refute unlisted_activity in activities
  110. assert private_activity in activities
  111. assert direct_activity in activities
  112. activities =
  113. ActivityPub.fetch_activities([], %{
  114. exclude_visibilities: "private",
  115. actor_id: user.ap_id
  116. })
  117. assert public_activity in activities
  118. assert unlisted_activity in activities
  119. refute private_activity in activities
  120. assert direct_activity in activities
  121. activities =
  122. ActivityPub.fetch_activities([], %{
  123. exclude_visibilities: "public",
  124. actor_id: user.ap_id
  125. })
  126. refute public_activity in activities
  127. assert unlisted_activity in activities
  128. assert private_activity in activities
  129. assert direct_activity in activities
  130. end
  131. end
  132. describe "building a user from his ap id" do
  133. test "it returns a user" do
  134. user_id = "http://mastodon.example.org/users/admin"
  135. {:ok, user} = ActivityPub.make_user_from_ap_id(user_id)
  136. assert user.ap_id == user_id
  137. assert user.nickname == "admin@mastodon.example.org"
  138. assert user.follower_address == "http://mastodon.example.org/users/admin/followers"
  139. end
  140. test "it returns a user that is invisible" do
  141. user_id = "http://mastodon.example.org/users/relay"
  142. {:ok, user} = ActivityPub.make_user_from_ap_id(user_id)
  143. assert User.invisible?(user)
  144. end
  145. test "it returns a user that accepts chat messages" do
  146. user_id = "http://mastodon.example.org/users/admin"
  147. {:ok, user} = ActivityPub.make_user_from_ap_id(user_id)
  148. assert user.accepts_chat_messages
  149. end
  150. test "works for guppe actors" do
  151. user_id = "https://gup.pe/u/bernie2020"
  152. Tesla.Mock.mock(fn
  153. %{method: :get, url: ^user_id} ->
  154. %Tesla.Env{
  155. status: 200,
  156. body: File.read!("test/fixtures/guppe-actor.json"),
  157. headers: [{"content-type", "application/activity+json"}]
  158. }
  159. end)
  160. {:ok, user} = ActivityPub.make_user_from_ap_id(user_id)
  161. assert user.name == "Bernie2020 group"
  162. assert user.actor_type == "Group"
  163. end
  164. test "works for bridgy actors" do
  165. user_id = "https://fed.brid.gy/jk.nipponalba.scot"
  166. Tesla.Mock.mock(fn
  167. %{method: :get, url: ^user_id} ->
  168. %Tesla.Env{
  169. status: 200,
  170. body: File.read!("test/fixtures/bridgy/actor.json"),
  171. headers: [{"content-type", "application/activity+json"}]
  172. }
  173. end)
  174. {:ok, user} = ActivityPub.make_user_from_ap_id(user_id)
  175. assert user.actor_type == "Person"
  176. assert user.avatar == %{
  177. "type" => "Image",
  178. "url" => [%{"href" => "https://jk.nipponalba.scot/images/profile.jpg"}],
  179. "name" => "profile picture"
  180. }
  181. assert user.banner == %{
  182. "type" => "Image",
  183. "url" => [%{"href" => "https://jk.nipponalba.scot/images/profile.jpg"}],
  184. "name" => "profile picture"
  185. }
  186. end
  187. test "fetches user featured collection" do
  188. ap_id = "https://example.com/users/lain"
  189. featured_url = "https://example.com/users/lain/collections/featured"
  190. user_data =
  191. "test/fixtures/users_mock/user.json"
  192. |> File.read!()
  193. |> String.replace("{{nickname}}", "lain")
  194. |> Jason.decode!()
  195. |> Map.put("featured", featured_url)
  196. |> Jason.encode!()
  197. object_id = Ecto.UUID.generate()
  198. featured_data =
  199. "test/fixtures/mastodon/collections/featured.json"
  200. |> File.read!()
  201. |> String.replace("{{domain}}", "example.com")
  202. |> String.replace("{{nickname}}", "lain")
  203. |> String.replace("{{object_id}}", object_id)
  204. object_url = "https://example.com/objects/#{object_id}"
  205. object_data =
  206. "test/fixtures/statuses/note.json"
  207. |> File.read!()
  208. |> String.replace("{{object_id}}", object_id)
  209. |> String.replace("{{nickname}}", "lain")
  210. Tesla.Mock.mock(fn
  211. %{
  212. method: :get,
  213. url: ^ap_id
  214. } ->
  215. %Tesla.Env{
  216. status: 200,
  217. body: user_data,
  218. headers: [{"content-type", "application/activity+json"}]
  219. }
  220. %{
  221. method: :get,
  222. url: ^featured_url
  223. } ->
  224. %Tesla.Env{
  225. status: 200,
  226. body: featured_data,
  227. headers: [{"content-type", "application/activity+json"}]
  228. }
  229. %{
  230. method: :get,
  231. url: ^object_url
  232. } ->
  233. %Tesla.Env{
  234. status: 200,
  235. body: object_data,
  236. headers: [{"content-type", "application/activity+json"}]
  237. }
  238. end)
  239. {:ok, user} = ActivityPub.make_user_from_ap_id(ap_id)
  240. assert_enqueued(
  241. worker: Pleroma.Workers.RemoteFetcherWorker,
  242. args: %{
  243. "op" => "fetch_remote",
  244. "id" => object_url,
  245. "depth" => 1
  246. }
  247. )
  248. # wait for oban
  249. Pleroma.Tests.ObanHelpers.perform_all()
  250. assert user.featured_address == featured_url
  251. assert Map.has_key?(user.pinned_objects, object_url)
  252. in_db = Pleroma.User.get_by_ap_id(ap_id)
  253. assert in_db.featured_address == featured_url
  254. assert Map.has_key?(user.pinned_objects, object_url)
  255. assert %{data: %{"id" => ^object_url}} = Object.get_by_ap_id(object_url)
  256. end
  257. test "fetches user featured collection without embedded object" do
  258. ap_id = "https://example.com/users/lain"
  259. featured_url = "https://example.com/users/lain/collections/featured"
  260. user_data =
  261. "test/fixtures/users_mock/user.json"
  262. |> File.read!()
  263. |> String.replace("{{nickname}}", "lain")
  264. |> Jason.decode!()
  265. |> Map.put("featured", featured_url)
  266. |> Jason.encode!()
  267. object_id = Ecto.UUID.generate()
  268. featured_data =
  269. "test/fixtures/mastodon/collections/external_featured.json"
  270. |> File.read!()
  271. |> String.replace("{{domain}}", "example.com")
  272. |> String.replace("{{nickname}}", "lain")
  273. |> String.replace("{{object_id}}", object_id)
  274. object_url = "https://example.com/objects/#{object_id}"
  275. object_data =
  276. "test/fixtures/statuses/note.json"
  277. |> File.read!()
  278. |> String.replace("{{object_id}}", object_id)
  279. |> String.replace("{{nickname}}", "lain")
  280. Tesla.Mock.mock(fn
  281. %{
  282. method: :get,
  283. url: ^ap_id
  284. } ->
  285. %Tesla.Env{
  286. status: 200,
  287. body: user_data,
  288. headers: [{"content-type", "application/activity+json"}]
  289. }
  290. %{
  291. method: :get,
  292. url: ^featured_url
  293. } ->
  294. %Tesla.Env{
  295. status: 200,
  296. body: featured_data,
  297. headers: [{"content-type", "application/activity+json"}]
  298. }
  299. %{
  300. method: :get,
  301. url: ^object_url
  302. } ->
  303. %Tesla.Env{
  304. status: 200,
  305. body: object_data,
  306. headers: [{"content-type", "application/activity+json"}]
  307. }
  308. end)
  309. {:ok, user} = ActivityPub.make_user_from_ap_id(ap_id)
  310. assert_enqueued(
  311. worker: Pleroma.Workers.RemoteFetcherWorker,
  312. args: %{
  313. "op" => "fetch_remote",
  314. "id" => object_url,
  315. "depth" => 1
  316. }
  317. )
  318. # wait for oban
  319. Pleroma.Tests.ObanHelpers.perform_all()
  320. assert user.featured_address == featured_url
  321. assert Map.has_key?(user.pinned_objects, object_url)
  322. in_db = Pleroma.User.get_by_ap_id(ap_id)
  323. assert in_db.featured_address == featured_url
  324. assert Map.has_key?(user.pinned_objects, object_url)
  325. assert %{data: %{"id" => ^object_url}} = Object.get_by_ap_id(object_url)
  326. end
  327. test "fetches user birthday information from misskey" do
  328. user_id = "https://misskey.io/@mkljczk"
  329. Tesla.Mock.mock(fn
  330. %{
  331. method: :get,
  332. url: ^user_id
  333. } ->
  334. %Tesla.Env{
  335. status: 200,
  336. body: File.read!("test/fixtures/birthdays/misskey-user.json"),
  337. headers: [{"content-type", "application/activity+json"}]
  338. }
  339. end)
  340. {:ok, user} = ActivityPub.make_user_from_ap_id(user_id)
  341. assert user.birthday == ~D[2001-02-12]
  342. end
  343. test "fetches avatar description" do
  344. user_id = "https://example.com/users/marcin"
  345. user_data =
  346. "test/fixtures/users_mock/user.json"
  347. |> File.read!()
  348. |> String.replace("{{nickname}}", "marcin")
  349. |> Jason.decode!()
  350. |> Map.delete("featured")
  351. |> Map.update("icon", %{}, fn image -> Map.put(image, "name", "image description") end)
  352. |> Jason.encode!()
  353. Tesla.Mock.mock(fn
  354. %{
  355. method: :get,
  356. url: ^user_id
  357. } ->
  358. %Tesla.Env{
  359. status: 200,
  360. body: user_data,
  361. headers: [{"content-type", "application/activity+json"}]
  362. }
  363. end)
  364. {:ok, user} = ActivityPub.make_user_from_ap_id(user_id)
  365. assert user.avatar["name"] == "image description"
  366. end
  367. end
  368. test "works with avatar/banner href as list" do
  369. user_id = "https://queef.in/cute_cat"
  370. user_data =
  371. "test/fixtures/users_mock/href_as_array.json"
  372. |> File.read!()
  373. |> Jason.decode!()
  374. |> Map.delete("featured")
  375. |> Jason.encode!()
  376. Tesla.Mock.mock(fn
  377. %{
  378. method: :get,
  379. url: ^user_id
  380. } ->
  381. %Tesla.Env{
  382. status: 200,
  383. body: user_data,
  384. headers: [{"content-type", "application/activity+json"}]
  385. }
  386. end)
  387. {:ok, user} = ActivityPub.make_user_from_ap_id(user_id)
  388. assert length(user.avatar["url"]) == 1
  389. assert length(user.banner["url"]) == 1
  390. assert user.avatar["url"] |> List.first() |> Map.fetch!("href") ==
  391. "https://queef.in/storage/profile.webp"
  392. assert user.banner["url"] |> List.first() |> Map.fetch!("href") ==
  393. "https://queef.in/storage/banner.gif"
  394. end
  395. test "it fetches the appropriate tag-restricted posts" do
  396. user = insert(:user)
  397. {:ok, status_one} = CommonAPI.post(user, %{status: ". #TEST"})
  398. {:ok, status_two} = CommonAPI.post(user, %{status: ". #essais"})
  399. {:ok, status_three} = CommonAPI.post(user, %{status: ". #test #Reject"})
  400. {:ok, status_four} = CommonAPI.post(user, %{status: ". #Any1 #any2"})
  401. {:ok, status_five} = CommonAPI.post(user, %{status: ". #Any2 #any1"})
  402. for hashtag_timeline_strategy <- [:enabled, :disabled] do
  403. clear_config([:features, :improved_hashtag_timeline], hashtag_timeline_strategy)
  404. fetch_one = ActivityPub.fetch_activities([], %{type: "Create", tag: "test"})
  405. fetch_two = ActivityPub.fetch_activities([], %{type: "Create", tag: ["TEST", "essais"]})
  406. fetch_three =
  407. ActivityPub.fetch_activities([], %{
  408. type: "Create",
  409. tag: ["test", "Essais"],
  410. tag_reject: ["reject"]
  411. })
  412. fetch_four =
  413. ActivityPub.fetch_activities([], %{
  414. type: "Create",
  415. tag: ["test"],
  416. tag_all: ["test", "REJECT"]
  417. })
  418. # Testing that deduplication (if needed) is done on DB (not Ecto) level; :limit is important
  419. fetch_five =
  420. ActivityPub.fetch_activities([], %{
  421. type: "Create",
  422. tag: ["ANY1", "any2"],
  423. limit: 2
  424. })
  425. fetch_six =
  426. ActivityPub.fetch_activities([], %{
  427. type: "Create",
  428. tag: ["any1", "Any2"],
  429. tag_all: [],
  430. tag_reject: []
  431. })
  432. # Regression test: passing empty lists as filter options shouldn't affect the results
  433. assert fetch_five == fetch_six
  434. [fetch_one, fetch_two, fetch_three, fetch_four, fetch_five] =
  435. Enum.map([fetch_one, fetch_two, fetch_three, fetch_four, fetch_five], fn statuses ->
  436. Enum.map(statuses, fn s -> Repo.preload(s, object: :hashtags) end)
  437. end)
  438. assert fetch_one == [status_one, status_three]
  439. assert fetch_two == [status_one, status_two, status_three]
  440. assert fetch_three == [status_one, status_two]
  441. assert fetch_four == [status_three]
  442. assert fetch_five == [status_four, status_five]
  443. end
  444. end
  445. describe "insertion" do
  446. test "drops activities beyond a certain limit" do
  447. limit = Config.get([:instance, :remote_limit])
  448. random_text =
  449. :crypto.strong_rand_bytes(limit + 1)
  450. |> Base.encode64()
  451. |> binary_part(0, limit + 1)
  452. data = %{
  453. "ok" => true,
  454. "object" => %{
  455. "content" => random_text
  456. }
  457. }
  458. assert {:error, :remote_limit} = ActivityPub.insert(data)
  459. end
  460. test "doesn't drop activities with content being null" do
  461. user = insert(:user)
  462. data = %{
  463. "actor" => user.ap_id,
  464. "to" => [],
  465. "object" => %{
  466. "actor" => user.ap_id,
  467. "to" => [],
  468. "type" => "Note",
  469. "content" => nil
  470. }
  471. }
  472. assert {:ok, _} = ActivityPub.insert(data)
  473. end
  474. test "returns the activity if one with the same id is already in" do
  475. activity = insert(:note_activity)
  476. {:ok, new_activity} = ActivityPub.insert(activity.data)
  477. assert activity.id == new_activity.id
  478. end
  479. test "inserts a given map into the activity database, giving it an id if it has none." do
  480. user = insert(:user)
  481. data = %{
  482. "actor" => user.ap_id,
  483. "to" => [],
  484. "object" => %{
  485. "actor" => user.ap_id,
  486. "to" => [],
  487. "type" => "Note",
  488. "content" => "hey"
  489. }
  490. }
  491. {:ok, %Activity{} = activity} = ActivityPub.insert(data)
  492. assert activity.data["ok"] == data["ok"]
  493. assert is_binary(activity.data["id"])
  494. given_id = "bla"
  495. data = %{
  496. "id" => given_id,
  497. "actor" => user.ap_id,
  498. "to" => [],
  499. "context" => "blabla",
  500. "object" => %{
  501. "actor" => user.ap_id,
  502. "to" => [],
  503. "type" => "Note",
  504. "content" => "hey"
  505. }
  506. }
  507. {:ok, %Activity{} = activity} = ActivityPub.insert(data)
  508. assert activity.data["ok"] == data["ok"]
  509. assert activity.data["id"] == given_id
  510. assert activity.data["context"] == "blabla"
  511. end
  512. test "adds a context when none is there" do
  513. user = insert(:user)
  514. data = %{
  515. "actor" => user.ap_id,
  516. "to" => [],
  517. "object" => %{
  518. "actor" => user.ap_id,
  519. "to" => [],
  520. "type" => "Note",
  521. "content" => "hey"
  522. }
  523. }
  524. {:ok, %Activity{} = activity} = ActivityPub.insert(data)
  525. object = Pleroma.Object.normalize(activity, fetch: false)
  526. assert is_binary(activity.data["context"])
  527. assert is_binary(object.data["context"])
  528. end
  529. test "adds an id to a given object if it lacks one and is a note and inserts it to the object database" do
  530. user = insert(:user)
  531. data = %{
  532. "actor" => user.ap_id,
  533. "to" => [],
  534. "object" => %{
  535. "actor" => user.ap_id,
  536. "to" => [],
  537. "type" => "Note",
  538. "content" => "hey"
  539. }
  540. }
  541. {:ok, %Activity{} = activity} = ActivityPub.insert(data)
  542. assert object = Object.normalize(activity, fetch: false)
  543. assert is_binary(object.data["id"])
  544. end
  545. end
  546. describe "listen activities" do
  547. test "does not increase user note count" do
  548. user = insert(:user)
  549. {:ok, activity} =
  550. ActivityPub.listen(%{
  551. to: ["https://www.w3.org/ns/activitystreams#Public"],
  552. actor: user,
  553. context: "",
  554. object: %{
  555. "actor" => user.ap_id,
  556. "to" => ["https://www.w3.org/ns/activitystreams#Public"],
  557. "artist" => "lain",
  558. "title" => "lain radio episode 1",
  559. "length" => 180_000,
  560. "type" => "Audio"
  561. }
  562. })
  563. assert activity.actor == user.ap_id
  564. user = User.get_cached_by_id(user.id)
  565. assert user.note_count == 0
  566. end
  567. test "can be fetched into a timeline" do
  568. _listen_activity_1 = insert(:listen)
  569. _listen_activity_2 = insert(:listen)
  570. _listen_activity_3 = insert(:listen)
  571. timeline = ActivityPub.fetch_activities([], %{type: ["Listen"]})
  572. assert length(timeline) == 3
  573. end
  574. end
  575. describe "create activities" do
  576. setup do
  577. [user: insert(:user)]
  578. end
  579. test "it reverts create", %{user: user} do
  580. with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
  581. assert {:error, :reverted} =
  582. ActivityPub.create(%{
  583. to: ["user1", "user2"],
  584. actor: user,
  585. context: "",
  586. object: %{
  587. "to" => ["user1", "user2"],
  588. "type" => "Note",
  589. "content" => "testing"
  590. }
  591. })
  592. end
  593. assert Repo.aggregate(Activity, :count, :id) == 0
  594. assert Repo.aggregate(Object, :count, :id) == 0
  595. end
  596. test "creates activity if expiration is not configured and expires_at is not passed", %{
  597. user: user
  598. } do
  599. clear_config([Pleroma.Workers.PurgeExpiredActivity, :enabled], false)
  600. assert {:ok, _} =
  601. ActivityPub.create(%{
  602. to: ["user1", "user2"],
  603. actor: user,
  604. context: "",
  605. object: %{
  606. "to" => ["user1", "user2"],
  607. "type" => "Note",
  608. "content" => "testing"
  609. }
  610. })
  611. end
  612. test "rejects activity if expires_at present but expiration is not configured", %{user: user} do
  613. clear_config([Pleroma.Workers.PurgeExpiredActivity, :enabled], false)
  614. assert {:error, :expired_activities_disabled} =
  615. ActivityPub.create(%{
  616. to: ["user1", "user2"],
  617. actor: user,
  618. context: "",
  619. object: %{
  620. "to" => ["user1", "user2"],
  621. "type" => "Note",
  622. "content" => "testing"
  623. },
  624. additional: %{
  625. "expires_at" => DateTime.utc_now()
  626. }
  627. })
  628. assert Repo.aggregate(Activity, :count, :id) == 0
  629. assert Repo.aggregate(Object, :count, :id) == 0
  630. end
  631. test "removes doubled 'to' recipients", %{user: user} do
  632. {:ok, activity} =
  633. ActivityPub.create(%{
  634. to: ["user1", "user1", "user2"],
  635. actor: user,
  636. context: "",
  637. object: %{
  638. "to" => ["user1", "user1", "user2"],
  639. "type" => "Note",
  640. "content" => "testing"
  641. }
  642. })
  643. assert activity.data["to"] == ["user1", "user2"]
  644. assert activity.actor == user.ap_id
  645. assert activity.recipients == ["user1", "user2", user.ap_id]
  646. end
  647. test "increases user note count only for public activities", %{user: user} do
  648. {:ok, _} =
  649. CommonAPI.post(User.get_cached_by_id(user.id), %{
  650. status: "1",
  651. visibility: "public"
  652. })
  653. {:ok, _} =
  654. CommonAPI.post(User.get_cached_by_id(user.id), %{
  655. status: "2",
  656. visibility: "unlisted"
  657. })
  658. {:ok, _} =
  659. CommonAPI.post(User.get_cached_by_id(user.id), %{
  660. status: "2",
  661. visibility: "private"
  662. })
  663. {:ok, _} =
  664. CommonAPI.post(User.get_cached_by_id(user.id), %{
  665. status: "3",
  666. visibility: "direct"
  667. })
  668. user = User.get_cached_by_id(user.id)
  669. assert user.note_count == 2
  670. end
  671. test "increases replies count", %{user: user} do
  672. user2 = insert(:user)
  673. {:ok, activity} = CommonAPI.post(user, %{status: "1", visibility: "public"})
  674. ap_id = activity.data["id"]
  675. reply_data = %{status: "1", in_reply_to_status_id: activity.id}
  676. # public
  677. {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, :visibility, "public"))
  678. assert %{data: _data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
  679. assert object.data["repliesCount"] == 1
  680. # unlisted
  681. {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, :visibility, "unlisted"))
  682. assert %{data: _data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
  683. assert object.data["repliesCount"] == 2
  684. # private
  685. {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, :visibility, "private"))
  686. assert %{data: _data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
  687. assert object.data["repliesCount"] == 2
  688. # direct
  689. {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, :visibility, "direct"))
  690. assert %{data: _data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
  691. assert object.data["repliesCount"] == 2
  692. end
  693. test "increases quotes count", %{user: user} do
  694. user2 = insert(:user)
  695. {:ok, activity} = CommonAPI.post(user, %{status: "1", visibility: "public"})
  696. ap_id = activity.data["id"]
  697. quote_data = %{status: "1", quoted_status_id: activity.id}
  698. # public
  699. {:ok, _} = CommonAPI.post(user2, Map.put(quote_data, :visibility, "public"))
  700. assert %{data: _data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
  701. assert object.data["quotesCount"] == 1
  702. # unlisted
  703. {:ok, _} = CommonAPI.post(user2, Map.put(quote_data, :visibility, "unlisted"))
  704. assert %{data: _data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
  705. assert object.data["quotesCount"] == 2
  706. # private
  707. {:ok, _} = CommonAPI.post(user2, Map.put(quote_data, :visibility, "private"))
  708. assert %{data: _data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
  709. assert object.data["quotesCount"] == 2
  710. # direct
  711. {:ok, _} = CommonAPI.post(user2, Map.put(quote_data, :visibility, "direct"))
  712. assert %{data: _data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
  713. assert object.data["quotesCount"] == 2
  714. end
  715. end
  716. describe "fetch activities for recipients" do
  717. test "retrieve the activities for certain recipients" do
  718. {:ok, activity_one} = ActivityBuilder.insert(%{"to" => ["someone"]})
  719. {:ok, activity_two} = ActivityBuilder.insert(%{"to" => ["someone_else"]})
  720. {:ok, _activity_three} = ActivityBuilder.insert(%{"to" => ["noone"]})
  721. activities = ActivityPub.fetch_activities(["someone", "someone_else"])
  722. assert length(activities) == 2
  723. assert activities == [activity_one, activity_two]
  724. end
  725. end
  726. describe "fetch activities for followed hashtags" do
  727. test "it should return public activities that reference a given hashtag" do
  728. hashtag = insert(:hashtag, name: "tenshi")
  729. user = insert(:user)
  730. other_user = insert(:user)
  731. {:ok, normally_visible} =
  732. CommonAPI.post(other_user, %{status: "hello :)", visibility: "public"})
  733. {:ok, public} = CommonAPI.post(user, %{status: "maji #tenshi", visibility: "public"})
  734. {:ok, _unrelated} = CommonAPI.post(user, %{status: "dai #tensh", visibility: "public"})
  735. {:ok, unlisted} = CommonAPI.post(user, %{status: "maji #tenshi", visibility: "unlisted"})
  736. {:ok, _private} = CommonAPI.post(user, %{status: "maji #tenshi", visibility: "private"})
  737. activities =
  738. ActivityPub.fetch_activities([other_user.follower_address], %{
  739. followed_hashtags: [hashtag.id]
  740. })
  741. assert length(activities) == 3
  742. normal_id = normally_visible.id
  743. public_id = public.id
  744. unlisted_id = unlisted.id
  745. assert [%{id: ^normal_id}, %{id: ^public_id}, %{id: ^unlisted_id}] = activities
  746. end
  747. end
  748. describe "fetch activities in context" do
  749. test "retrieves activities that have a given context" do
  750. {:ok, activity} = ActivityBuilder.insert(%{"type" => "Create", "context" => "2hu"})
  751. {:ok, activity_two} = ActivityBuilder.insert(%{"type" => "Create", "context" => "2hu"})
  752. {:ok, _activity_three} = ActivityBuilder.insert(%{"type" => "Create", "context" => "3hu"})
  753. {:ok, _activity_four} = ActivityBuilder.insert(%{"type" => "Announce", "context" => "2hu"})
  754. activity_five = insert(:note_activity)
  755. user = insert(:user)
  756. {:ok, _user_relationship} = User.block(user, %{ap_id: activity_five.data["actor"]})
  757. activities = ActivityPub.fetch_activities_for_context("2hu", %{blocking_user: user})
  758. assert activities == [activity_two, activity]
  759. end
  760. test "doesn't return activities with filtered words" do
  761. user = insert(:user)
  762. user_two = insert(:user)
  763. insert(:filter, user: user, phrase: "test", hide: true)
  764. {:ok, %{id: id1, data: %{"context" => context}}} = CommonAPI.post(user, %{status: "1"})
  765. {:ok, %{id: id2}} = CommonAPI.post(user_two, %{status: "2", in_reply_to_status_id: id1})
  766. {:ok, %{id: id3} = user_activity} =
  767. CommonAPI.post(user, %{status: "3 test?", in_reply_to_status_id: id2})
  768. {:ok, %{id: id4} = filtered_activity} =
  769. CommonAPI.post(user_two, %{status: "4 test!", in_reply_to_status_id: id3})
  770. {:ok, _} = CommonAPI.post(user, %{status: "5", in_reply_to_status_id: id4})
  771. activities =
  772. context
  773. |> ActivityPub.fetch_activities_for_context(%{user: user})
  774. |> Enum.map(& &1.id)
  775. assert length(activities) == 4
  776. assert user_activity.id in activities
  777. refute filtered_activity.id in activities
  778. end
  779. end
  780. test "doesn't return blocked activities" do
  781. activity_one = insert(:note_activity)
  782. activity_two = insert(:note_activity)
  783. activity_three = insert(:note_activity)
  784. user = insert(:user)
  785. booster = insert(:user)
  786. {:ok, _user_relationship} = User.block(user, %{ap_id: activity_one.data["actor"]})
  787. activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true})
  788. assert Enum.member?(activities, activity_two)
  789. assert Enum.member?(activities, activity_three)
  790. refute Enum.member?(activities, activity_one)
  791. {:ok, _user_block} = User.unblock(user, %{ap_id: activity_one.data["actor"]})
  792. activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true})
  793. assert Enum.member?(activities, activity_two)
  794. assert Enum.member?(activities, activity_three)
  795. assert Enum.member?(activities, activity_one)
  796. {:ok, _user_relationship} = User.block(user, %{ap_id: activity_three.data["actor"]})
  797. {:ok, %{data: %{"object" => id}}} = CommonAPI.repeat(activity_three.id, booster)
  798. %Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)
  799. activity_three = Activity.get_by_id(activity_three.id)
  800. activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true})
  801. assert Enum.member?(activities, activity_two)
  802. refute Enum.member?(activities, activity_three)
  803. refute Enum.member?(activities, boost_activity)
  804. assert Enum.member?(activities, activity_one)
  805. activities = ActivityPub.fetch_activities([], %{blocking_user: nil, skip_preload: true})
  806. assert Enum.member?(activities, activity_two)
  807. assert Enum.member?(activities, activity_three)
  808. assert Enum.member?(activities, boost_activity)
  809. assert Enum.member?(activities, activity_one)
  810. end
  811. test "doesn't return activities from deactivated users" do
  812. _user = insert(:user)
  813. deactivated = insert(:user)
  814. active = insert(:user)
  815. {:ok, activity_one} = CommonAPI.post(deactivated, %{status: "hey!"})
  816. {:ok, activity_two} = CommonAPI.post(active, %{status: "yay!"})
  817. {:ok, _updated_user} = User.set_activation(deactivated, false)
  818. activities = ActivityPub.fetch_activities([], %{})
  819. refute Enum.member?(activities, activity_one)
  820. assert Enum.member?(activities, activity_two)
  821. end
  822. test "always see your own posts even when they address people you block" do
  823. user = insert(:user)
  824. blockee = insert(:user)
  825. {:ok, _} = User.block(user, blockee)
  826. {:ok, activity} = CommonAPI.post(user, %{status: "hey! @#{blockee.nickname}"})
  827. activities = ActivityPub.fetch_activities([], %{blocking_user: user})
  828. assert Enum.member?(activities, activity)
  829. end
  830. test "doesn't return transitive interactions concerning blocked users" do
  831. blocker = insert(:user)
  832. blockee = insert(:user)
  833. friend = insert(:user)
  834. {:ok, _user_relationship} = User.block(blocker, blockee)
  835. {:ok, activity_one} = CommonAPI.post(friend, %{status: "hey!"})
  836. {:ok, activity_two} = CommonAPI.post(friend, %{status: "hey! @#{blockee.nickname}"})
  837. {:ok, activity_three} = CommonAPI.post(blockee, %{status: "hey! @#{friend.nickname}"})
  838. {:ok, activity_four} = CommonAPI.post(blockee, %{status: "hey! @#{blocker.nickname}"})
  839. activities = ActivityPub.fetch_activities([], %{blocking_user: blocker})
  840. assert Enum.member?(activities, activity_one)
  841. refute Enum.member?(activities, activity_two)
  842. refute Enum.member?(activities, activity_three)
  843. refute Enum.member?(activities, activity_four)
  844. end
  845. test "doesn't return announce activities with blocked users in 'to'" do
  846. blocker = insert(:user)
  847. blockee = insert(:user)
  848. friend = insert(:user)
  849. {:ok, _user_relationship} = User.block(blocker, blockee)
  850. {:ok, activity_one} = CommonAPI.post(friend, %{status: "hey!"})
  851. {:ok, activity_two} = CommonAPI.post(blockee, %{status: "hey! @#{friend.nickname}"})
  852. {:ok, activity_three} = CommonAPI.repeat(activity_two.id, friend)
  853. activities =
  854. ActivityPub.fetch_activities([], %{blocking_user: blocker})
  855. |> Enum.map(fn act -> act.id end)
  856. assert Enum.member?(activities, activity_one.id)
  857. refute Enum.member?(activities, activity_two.id)
  858. refute Enum.member?(activities, activity_three.id)
  859. end
  860. test "doesn't return announce activities with blocked users in 'cc'" do
  861. blocker = insert(:user)
  862. blockee = insert(:user)
  863. friend = insert(:user)
  864. {:ok, _user_relationship} = User.block(blocker, blockee)
  865. {:ok, activity_one} = CommonAPI.post(friend, %{status: "hey!"})
  866. {:ok, activity_two} = CommonAPI.post(blockee, %{status: "hey! @#{friend.nickname}"})
  867. assert object = Pleroma.Object.normalize(activity_two, fetch: false)
  868. data = %{
  869. "actor" => friend.ap_id,
  870. "object" => object.data["id"],
  871. "context" => object.data["context"],
  872. "type" => "Announce",
  873. "to" => ["https://www.w3.org/ns/activitystreams#Public"],
  874. "cc" => [blockee.ap_id]
  875. }
  876. assert {:ok, activity_three} = ActivityPub.insert(data)
  877. activities =
  878. ActivityPub.fetch_activities([], %{blocking_user: blocker})
  879. |> Enum.map(fn act -> act.id end)
  880. assert Enum.member?(activities, activity_one.id)
  881. refute Enum.member?(activities, activity_two.id)
  882. refute Enum.member?(activities, activity_three.id)
  883. end
  884. test "doesn't return activities from blocked domains" do
  885. domain = "dogwhistle.zone"
  886. domain_user = insert(:user, %{ap_id: "https://#{domain}/@pundit"})
  887. note = insert(:note, %{data: %{"actor" => domain_user.ap_id}})
  888. activity = insert(:note_activity, %{note: note})
  889. user = insert(:user)
  890. {:ok, user} = User.block_domain(user, domain)
  891. activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true})
  892. refute activity in activities
  893. followed_user = insert(:user)
  894. CommonAPI.follow(followed_user, user)
  895. {:ok, repeat_activity} = CommonAPI.repeat(activity.id, followed_user)
  896. activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true})
  897. refute repeat_activity in activities
  898. end
  899. test "see your own posts even when they address actors from blocked domains" do
  900. user = insert(:user)
  901. domain = "dogwhistle.zone"
  902. domain_user = insert(:user, %{ap_id: "https://#{domain}/@pundit"})
  903. {:ok, user} = User.block_domain(user, domain)
  904. {:ok, activity} = CommonAPI.post(user, %{status: "hey! @#{domain_user.nickname}"})
  905. activities = ActivityPub.fetch_activities([], %{blocking_user: user})
  906. assert Enum.member?(activities, activity)
  907. end
  908. test "does return activities from followed users on blocked domains" do
  909. domain = "meanies.social"
  910. domain_user = insert(:user, %{ap_id: "https://#{domain}/@pundit"})
  911. blocker = insert(:user)
  912. {:ok, blocker, domain_user} = User.follow(blocker, domain_user)
  913. {:ok, blocker} = User.block_domain(blocker, domain)
  914. assert User.following?(blocker, domain_user)
  915. assert User.blocks_domain?(blocker, domain_user)
  916. refute User.blocks?(blocker, domain_user)
  917. note = insert(:note, %{data: %{"actor" => domain_user.ap_id}})
  918. activity = insert(:note_activity, %{note: note})
  919. activities = ActivityPub.fetch_activities([], %{blocking_user: blocker, skip_preload: true})
  920. assert activity in activities
  921. # And check that if the guy we DO follow boosts someone else from their domain,
  922. # that should be hidden
  923. another_user = insert(:user, %{ap_id: "https://#{domain}/@meanie2"})
  924. bad_note = insert(:note, %{data: %{"actor" => another_user.ap_id}})
  925. bad_activity = insert(:note_activity, %{note: bad_note})
  926. {:ok, repeat_activity} = CommonAPI.repeat(bad_activity.id, domain_user)
  927. activities = ActivityPub.fetch_activities([], %{blocking_user: blocker, skip_preload: true})
  928. refute repeat_activity in activities
  929. end
  930. test "returns your own posts regardless of mute" do
  931. user = insert(:user)
  932. muted = insert(:user)
  933. {:ok, muted_post} = CommonAPI.post(muted, %{status: "Im stupid"})
  934. {:ok, reply} =
  935. CommonAPI.post(user, %{status: "I'm muting you", in_reply_to_status_id: muted_post.id})
  936. {:ok, _} = User.mute(user, muted)
  937. [activity] = ActivityPub.fetch_activities([], %{muting_user: user, skip_preload: true})
  938. assert activity.id == reply.id
  939. end
  940. test "doesn't return muted activities" do
  941. activity_one = insert(:note_activity)
  942. activity_two = insert(:note_activity)
  943. activity_three = insert(:note_activity)
  944. user = insert(:user)
  945. booster = insert(:user)
  946. activity_one_actor = User.get_by_ap_id(activity_one.data["actor"])
  947. {:ok, _user_relationships} = User.mute(user, activity_one_actor)
  948. activities = ActivityPub.fetch_activities([], %{muting_user: user, skip_preload: true})
  949. assert Enum.member?(activities, activity_two)
  950. assert Enum.member?(activities, activity_three)
  951. refute Enum.member?(activities, activity_one)
  952. # Calling with 'with_muted' will deliver muted activities, too.
  953. activities =
  954. ActivityPub.fetch_activities([], %{
  955. muting_user: user,
  956. with_muted: true,
  957. skip_preload: true
  958. })
  959. assert Enum.member?(activities, activity_two)
  960. assert Enum.member?(activities, activity_three)
  961. assert Enum.member?(activities, activity_one)
  962. {:ok, _user_mute} = User.unmute(user, activity_one_actor)
  963. activities = ActivityPub.fetch_activities([], %{muting_user: user, skip_preload: true})
  964. assert Enum.member?(activities, activity_two)
  965. assert Enum.member?(activities, activity_three)
  966. assert Enum.member?(activities, activity_one)
  967. activity_three_actor = User.get_by_ap_id(activity_three.data["actor"])
  968. {:ok, _user_relationships} = User.mute(user, activity_three_actor)
  969. {:ok, %{data: %{"object" => id}}} = CommonAPI.repeat(activity_three.id, booster)
  970. %Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)
  971. activity_three = Activity.get_by_id(activity_three.id)
  972. activities = ActivityPub.fetch_activities([], %{muting_user: user, skip_preload: true})
  973. assert Enum.member?(activities, activity_two)
  974. refute Enum.member?(activities, activity_three)
  975. refute Enum.member?(activities, boost_activity)
  976. assert Enum.member?(activities, activity_one)
  977. activities = ActivityPub.fetch_activities([], %{muting_user: nil, skip_preload: true})
  978. assert Enum.member?(activities, activity_two)
  979. assert Enum.member?(activities, activity_three)
  980. assert Enum.member?(activities, boost_activity)
  981. assert Enum.member?(activities, activity_one)
  982. end
  983. test "doesn't return thread muted activities" do
  984. user = insert(:user)
  985. _activity_one = insert(:note_activity)
  986. note_two = insert(:note, data: %{"context" => "suya.."})
  987. activity_two = insert(:note_activity, note: note_two)
  988. {:ok, _activity_two} = CommonAPI.add_mute(activity_two, user)
  989. assert [_activity_one] = ActivityPub.fetch_activities([], %{muting_user: user})
  990. end
  991. test "returns thread muted activities when with_muted is set" do
  992. user = insert(:user)
  993. _activity_one = insert(:note_activity)
  994. note_two = insert(:note, data: %{"context" => "suya.."})
  995. activity_two = insert(:note_activity, note: note_two)
  996. {:ok, _activity_two} = CommonAPI.add_mute(activity_two, user)
  997. assert [_activity_two, _activity_one] =
  998. ActivityPub.fetch_activities([], %{muting_user: user, with_muted: true})
  999. end
  1000. test "does include announces on request" do
  1001. activity_three = insert(:note_activity)
  1002. user = insert(:user)
  1003. booster = insert(:user)
  1004. {:ok, user, booster} = User.follow(user, booster)
  1005. {:ok, announce} = CommonAPI.repeat(activity_three.id, booster)
  1006. [announce_activity] = ActivityPub.fetch_activities([user.ap_id | User.following(user)])
  1007. assert announce_activity.id == announce.id
  1008. end
  1009. test "excludes reblogs on request" do
  1010. user = insert(:user)
  1011. {:ok, expected_activity} = ActivityBuilder.insert(%{"type" => "Create"}, %{:user => user})
  1012. {:ok, _} = ActivityBuilder.insert(%{"type" => "Announce"}, %{:user => user})
  1013. [activity] = ActivityPub.fetch_user_activities(user, nil, %{exclude_reblogs: true})
  1014. assert activity == expected_activity
  1015. end
  1016. test "includes only reblogs on request" do
  1017. user = insert(:user)
  1018. {:ok, _} = ActivityBuilder.insert(%{"type" => "Create"}, %{:user => user})
  1019. {:ok, expected_activity} = ActivityBuilder.insert(%{"type" => "Announce"}, %{:user => user})
  1020. [activity] = ActivityPub.fetch_user_activities(user, nil, %{only_reblogs: true})
  1021. assert activity == expected_activity
  1022. end
  1023. describe "irreversible filters" do
  1024. setup do
  1025. user = insert(:user)
  1026. user_two = insert(:user)
  1027. insert(:filter, user: user_two, phrase: "cofe", hide: true)
  1028. insert(:filter, user: user_two, phrase: "ok boomer", hide: true)
  1029. insert(:filter, user: user_two, phrase: "test", hide: false)
  1030. params = %{
  1031. type: ["Create", "Announce"],
  1032. user: user_two
  1033. }
  1034. {:ok, %{user: user, user_two: user_two, params: params}}
  1035. end
  1036. test "it returns statuses if they don't contain exact filter words", %{
  1037. user: user,
  1038. params: params
  1039. } do
  1040. {:ok, _} = CommonAPI.post(user, %{status: "hey"})
  1041. {:ok, _} = CommonAPI.post(user, %{status: "got cofefe?"})
  1042. {:ok, _} = CommonAPI.post(user, %{status: "I am not a boomer"})
  1043. {:ok, _} = CommonAPI.post(user, %{status: "ok boomers"})
  1044. {:ok, _} = CommonAPI.post(user, %{status: "ccofee is not a word"})
  1045. {:ok, _} = CommonAPI.post(user, %{status: "this is a test"})
  1046. activities = ActivityPub.fetch_activities([], params)
  1047. assert Enum.count(activities) == 6
  1048. end
  1049. test "it does not filter user's own statuses", %{user_two: user_two, params: params} do
  1050. {:ok, _} = CommonAPI.post(user_two, %{status: "Give me some cofe!"})
  1051. {:ok, _} = CommonAPI.post(user_two, %{status: "ok boomer"})
  1052. activities = ActivityPub.fetch_activities([], params)
  1053. assert Enum.count(activities) == 2
  1054. end
  1055. test "it excludes statuses with filter words", %{user: user, params: params} do
  1056. {:ok, _} = CommonAPI.post(user, %{status: "Give me some cofe!"})
  1057. {:ok, _} = CommonAPI.post(user, %{status: "ok boomer"})
  1058. {:ok, _} = CommonAPI.post(user, %{status: "is it a cOfE?"})
  1059. {:ok, _} = CommonAPI.post(user, %{status: "cofe is all I need"})
  1060. {:ok, _} = CommonAPI.post(user, %{status: "— ok BOOMER\n"})
  1061. activities = ActivityPub.fetch_activities([], params)
  1062. assert Enum.empty?(activities)
  1063. end
  1064. test "it returns all statuses if user does not have any filters" do
  1065. another_user = insert(:user)
  1066. {:ok, _} = CommonAPI.post(another_user, %{status: "got cofe?"})
  1067. {:ok, _} = CommonAPI.post(another_user, %{status: "test!"})
  1068. activities =
  1069. ActivityPub.fetch_activities([], %{
  1070. type: ["Create", "Announce"],
  1071. user: another_user
  1072. })
  1073. assert Enum.count(activities) == 2
  1074. end
  1075. end
  1076. describe "public fetch activities" do
  1077. test "doesn't retrieve unlisted activities" do
  1078. user = insert(:user)
  1079. {:ok, _unlisted_activity} = CommonAPI.post(user, %{status: "yeah", visibility: "unlisted"})
  1080. {:ok, listed_activity} = CommonAPI.post(user, %{status: "yeah"})
  1081. [activity] = ActivityPub.fetch_public_activities()
  1082. assert activity == listed_activity
  1083. end
  1084. test "retrieves public activities" do
  1085. _activities = ActivityPub.fetch_public_activities()
  1086. %{public: public} = ActivityBuilder.public_and_non_public()
  1087. activities = ActivityPub.fetch_public_activities()
  1088. assert length(activities) == 1
  1089. assert Enum.at(activities, 0) == public
  1090. end
  1091. test "retrieves a maximum of 20 activities" do
  1092. ActivityBuilder.insert_list(10)
  1093. expected_activities = ActivityBuilder.insert_list(20)
  1094. activities = ActivityPub.fetch_public_activities()
  1095. assert collect_ids(activities) == collect_ids(expected_activities)
  1096. assert length(activities) == 20
  1097. end
  1098. test "retrieves ids starting from a since_id" do
  1099. activities = ActivityBuilder.insert_list(30)
  1100. expected_activities = ActivityBuilder.insert_list(10)
  1101. since_id = List.last(activities).id
  1102. activities = ActivityPub.fetch_public_activities(%{since_id: since_id})
  1103. assert collect_ids(activities) == collect_ids(expected_activities)
  1104. assert length(activities) == 10
  1105. end
  1106. test "retrieves ids up to max_id" do
  1107. ActivityBuilder.insert_list(10)
  1108. expected_activities = ActivityBuilder.insert_list(20)
  1109. %{id: max_id} =
  1110. 10
  1111. |> ActivityBuilder.insert_list()
  1112. |> List.first()
  1113. activities = ActivityPub.fetch_public_activities(%{max_id: max_id})
  1114. assert length(activities) == 20
  1115. assert collect_ids(activities) == collect_ids(expected_activities)
  1116. end
  1117. test "paginates via offset/limit" do
  1118. _first_part_activities = ActivityBuilder.insert_list(10)
  1119. second_part_activities = ActivityBuilder.insert_list(10)
  1120. later_activities = ActivityBuilder.insert_list(10)
  1121. activities = ActivityPub.fetch_public_activities(%{page: "2", page_size: "20"}, :offset)
  1122. assert length(activities) == 20
  1123. assert collect_ids(activities) ==
  1124. collect_ids(second_part_activities) ++ collect_ids(later_activities)
  1125. end
  1126. test "doesn't return reblogs for users for whom reblogs have been muted" do
  1127. activity = insert(:note_activity)
  1128. user = insert(:user)
  1129. booster = insert(:user)
  1130. {:ok, _reblog_mute} = CommonAPI.hide_reblogs(booster, user)
  1131. {:ok, activity} = CommonAPI.repeat(activity.id, booster)
  1132. activities = ActivityPub.fetch_activities([], %{muting_user: user})
  1133. refute Enum.any?(activities, fn %{id: id} -> id == activity.id end)
  1134. end
  1135. test "returns reblogs for users for whom reblogs have not been muted" do
  1136. activity = insert(:note_activity)
  1137. user = insert(:user)
  1138. booster = insert(:user)
  1139. {:ok, _reblog_mute} = CommonAPI.hide_reblogs(booster, user)
  1140. {:ok, _reblog_mute} = CommonAPI.show_reblogs(booster, user)
  1141. {:ok, activity} = CommonAPI.repeat(activity.id, booster)
  1142. activities = ActivityPub.fetch_activities([], %{muting_user: user})
  1143. assert Enum.any?(activities, fn %{id: id} -> id == activity.id end)
  1144. end
  1145. end
  1146. describe "uploading files" do
  1147. setup do
  1148. test_file = %Plug.Upload{
  1149. content_type: "image/jpeg",
  1150. path: Path.absname("test/fixtures/image.jpg"),
  1151. filename: "an_image.jpg"
  1152. }
  1153. %{test_file: test_file}
  1154. end
  1155. test "strips / from filename", %{test_file: file} do
  1156. file = %Plug.Upload{file | filename: "../../../../../nested/bad.jpg"}
  1157. {:ok, %Object{} = object} = ActivityPub.upload(file)
  1158. [%{"href" => href}] = object.data["url"]
  1159. assert Regex.match?(~r"/bad.jpg$", href)
  1160. refute Regex.match?(~r"/nested/", href)
  1161. end
  1162. test "sets a description if given", %{test_file: file} do
  1163. {:ok, %Object{} = object} = ActivityPub.upload(file, description: "a cool file")
  1164. assert object.data["name"] == "a cool file"
  1165. end
  1166. test "it sets the default description depending on the configuration", %{test_file: file} do
  1167. clear_config([Pleroma.Upload, :default_description])
  1168. clear_config([Pleroma.Upload, :default_description], nil)
  1169. {:ok, %Object{} = object} = ActivityPub.upload(file)
  1170. assert object.data["name"] == ""
  1171. clear_config([Pleroma.Upload, :default_description], :filename)
  1172. {:ok, %Object{} = object} = ActivityPub.upload(file)
  1173. assert object.data["name"] == "an_image.jpg"
  1174. clear_config([Pleroma.Upload, :default_description], "unnamed attachment")
  1175. {:ok, %Object{} = object} = ActivityPub.upload(file)
  1176. assert object.data["name"] == "unnamed attachment"
  1177. end
  1178. test "copies the file to the configured folder", %{test_file: file} do
  1179. clear_config([Pleroma.Upload, :default_description], :filename)
  1180. {:ok, %Object{} = object} = ActivityPub.upload(file)
  1181. assert object.data["name"] == "an_image.jpg"
  1182. end
  1183. test "works with base64 encoded images" do
  1184. file = %{
  1185. img: data_uri()
  1186. }
  1187. {:ok, %Object{}} = ActivityPub.upload(file)
  1188. end
  1189. end
  1190. describe "fetch the latest Follow" do
  1191. test "fetches the latest Follow activity" do
  1192. %Activity{data: %{"type" => "Follow"}} = activity = insert(:follow_activity)
  1193. follower = Repo.get_by(User, ap_id: activity.data["actor"])
  1194. followed = Repo.get_by(User, ap_id: activity.data["object"])
  1195. assert activity == Utils.fetch_latest_follow(follower, followed)
  1196. end
  1197. end
  1198. describe "unfollowing" do
  1199. test "it reverts unfollow activity" do
  1200. follower = insert(:user)
  1201. followed = insert(:user)
  1202. {:ok, _, _, follow_activity} = CommonAPI.follow(followed, follower)
  1203. with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
  1204. assert {:error, :reverted} = ActivityPub.unfollow(follower, followed)
  1205. end
  1206. activity = Activity.get_by_id(follow_activity.id)
  1207. assert activity.data["type"] == "Follow"
  1208. assert activity.data["actor"] == follower.ap_id
  1209. assert activity.data["object"] == followed.ap_id
  1210. end
  1211. test "creates an undo activity for the last follow" do
  1212. follower = insert(:user)
  1213. followed = insert(:user)
  1214. {:ok, _, _, follow_activity} = CommonAPI.follow(followed, follower)
  1215. {:ok, activity} = ActivityPub.unfollow(follower, followed)
  1216. assert activity.data["type"] == "Undo"
  1217. assert activity.data["actor"] == follower.ap_id
  1218. embedded_object = activity.data["object"]
  1219. assert is_map(embedded_object)
  1220. assert embedded_object["type"] == "Follow"
  1221. assert embedded_object["object"] == followed.ap_id
  1222. assert embedded_object["id"] == follow_activity.data["id"]
  1223. end
  1224. test "creates an undo activity for a pending follow request" do
  1225. follower = insert(:user)
  1226. followed = insert(:user, %{is_locked: true})
  1227. {:ok, _, _, follow_activity} = CommonAPI.follow(followed, follower)
  1228. {:ok, activity} = ActivityPub.unfollow(follower, followed)
  1229. assert activity.data["type"] == "Undo"
  1230. assert activity.data["actor"] == follower.ap_id
  1231. embedded_object = activity.data["object"]
  1232. assert is_map(embedded_object)
  1233. assert embedded_object["type"] == "Follow"
  1234. assert embedded_object["object"] == followed.ap_id
  1235. assert embedded_object["id"] == follow_activity.data["id"]
  1236. end
  1237. end
  1238. describe "timeline post-processing" do
  1239. test "it filters broken threads" do
  1240. user1 = insert(:user)
  1241. user2 = insert(:user)
  1242. user3 = insert(:user)
  1243. {:ok, user1, user3} = User.follow(user1, user3)
  1244. assert User.following?(user1, user3)
  1245. {:ok, user2, user3} = User.follow(user2, user3)
  1246. assert User.following?(user2, user3)
  1247. {:ok, user3, user2} = User.follow(user3, user2)
  1248. assert User.following?(user3, user2)
  1249. {:ok, public_activity} = CommonAPI.post(user3, %{status: "hi 1"})
  1250. {:ok, private_activity_1} = CommonAPI.post(user3, %{status: "hi 2", visibility: "private"})
  1251. {:ok, private_activity_2} =
  1252. CommonAPI.post(user2, %{
  1253. status: "hi 3",
  1254. visibility: "private",
  1255. in_reply_to_status_id: private_activity_1.id
  1256. })
  1257. {:ok, private_activity_3} =
  1258. CommonAPI.post(user3, %{
  1259. status: "hi 4",
  1260. visibility: "private",
  1261. in_reply_to_status_id: private_activity_2.id
  1262. })
  1263. activities =
  1264. ActivityPub.fetch_activities([user1.ap_id | User.following(user1)])
  1265. |> Enum.map(fn a -> a.id end)
  1266. private_activity_1 = Activity.get_by_ap_id_with_object(private_activity_1.data["id"])
  1267. assert [public_activity.id, private_activity_1.id, private_activity_3.id] == activities
  1268. assert length(activities) == 3
  1269. activities =
  1270. ActivityPub.fetch_activities([user1.ap_id | User.following(user1)], %{user: user1})
  1271. |> Enum.map(fn a -> a.id end)
  1272. assert [public_activity.id, private_activity_1.id] == activities
  1273. assert length(activities) == 2
  1274. end
  1275. end
  1276. describe "flag/1" do
  1277. setup do
  1278. reporter = insert(:user)
  1279. target_account = insert(:user)
  1280. content = "foobar"
  1281. {:ok, activity} = CommonAPI.post(target_account, %{status: content})
  1282. context = Utils.generate_context_id()
  1283. reporter_ap_id = reporter.ap_id
  1284. target_ap_id = target_account.ap_id
  1285. activity_ap_id = activity.data["id"]
  1286. object_ap_id = activity.object.data["id"]
  1287. activity_with_object = Activity.get_by_ap_id_with_object(activity_ap_id)
  1288. {:ok,
  1289. %{
  1290. reporter: reporter,
  1291. context: context,
  1292. target_account: target_account,
  1293. reported_activity: activity,
  1294. content: content,
  1295. activity_ap_id: activity_ap_id,
  1296. object_ap_id: object_ap_id,
  1297. activity_with_object: activity_with_object,
  1298. reporter_ap_id: reporter_ap_id,
  1299. target_ap_id: target_ap_id
  1300. }}
  1301. end
  1302. test "it can create a Flag activity",
  1303. %{
  1304. reporter: reporter,
  1305. context: context,
  1306. target_account: target_account,
  1307. reported_activity: reported_activity,
  1308. content: content,
  1309. object_ap_id: object_ap_id,
  1310. activity_with_object: activity_with_object,
  1311. reporter_ap_id: reporter_ap_id,
  1312. target_ap_id: target_ap_id
  1313. } do
  1314. assert {:ok, activity} =
  1315. ActivityPub.flag(%{
  1316. actor: reporter,
  1317. context: context,
  1318. account: target_account,
  1319. statuses: [reported_activity],
  1320. content: content
  1321. })
  1322. note_obj = %{
  1323. "type" => "Note",
  1324. "id" => object_ap_id,
  1325. "content" => content,
  1326. "published" => activity_with_object.object.data["published"],
  1327. "actor" =>
  1328. AccountView.render("show.json", %{user: target_account, skip_visibility_check: true})
  1329. }
  1330. assert %Activity{
  1331. actor: ^reporter_ap_id,
  1332. data: %{
  1333. "type" => "Flag",
  1334. "content" => ^content,
  1335. "context" => ^context,
  1336. "object" => [^target_ap_id, ^note_obj]
  1337. }
  1338. } = activity
  1339. end
  1340. test_with_mock "reverts on error",
  1341. %{
  1342. reporter: reporter,
  1343. context: context,
  1344. target_account: target_account,
  1345. reported_activity: reported_activity,
  1346. content: content
  1347. },
  1348. Utils,
  1349. [:passthrough],
  1350. maybe_federate: fn _ -> {:error, :reverted} end do
  1351. assert {:error, :reverted} =
  1352. ActivityPub.flag(%{
  1353. actor: reporter,
  1354. context: context,
  1355. account: target_account,
  1356. statuses: [reported_activity],
  1357. content: content
  1358. })
  1359. assert Repo.aggregate(Activity, :count, :id) == 1
  1360. assert Repo.aggregate(Object, :count, :id) == 1
  1361. assert Repo.aggregate(Notification, :count, :id) == 0
  1362. end
  1363. end
  1364. test "fetch_activities/2 returns activities addressed to a list " do
  1365. user = insert(:user)
  1366. member = insert(:user)
  1367. {:ok, list} = Pleroma.List.create("foo", user)
  1368. {:ok, list} = Pleroma.List.follow(list, member)
  1369. {:ok, activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"})
  1370. activity = Repo.preload(activity, :bookmark)
  1371. activity = %Activity{activity | thread_muted?: !!activity.thread_muted?}
  1372. assert ActivityPub.fetch_activities([], %{user: user}) == [activity]
  1373. end
  1374. def data_uri do
  1375. File.read!("test/fixtures/avatar_data_uri")
  1376. end
  1377. describe "fetch_activities_bounded" do
  1378. test "fetches private posts for followed users" do
  1379. user = insert(:user)
  1380. {:ok, activity} =
  1381. CommonAPI.post(user, %{
  1382. status: "thought I looked cute might delete later :3",
  1383. visibility: "private"
  1384. })
  1385. [result] = ActivityPub.fetch_activities_bounded([user.follower_address], [])
  1386. assert result.id == activity.id
  1387. end
  1388. test "fetches only public posts for other users" do
  1389. user = insert(:user)
  1390. {:ok, activity} = CommonAPI.post(user, %{status: "#cofe", visibility: "public"})
  1391. {:ok, _private_activity} =
  1392. CommonAPI.post(user, %{
  1393. status: "why is tenshi eating a corndog so cute?",
  1394. visibility: "private"
  1395. })
  1396. [result] = ActivityPub.fetch_activities_bounded([], [user.follower_address])
  1397. assert result.id == activity.id
  1398. end
  1399. end
  1400. describe "fetch_follow_information_for_user" do
  1401. test "synchronizes following/followers counters" do
  1402. user =
  1403. insert(:user,
  1404. local: false,
  1405. follower_address: "https://remote.org/users/fuser2/followers",
  1406. following_address: "https://remote.org/users/fuser2/following"
  1407. )
  1408. {:ok, info} = ActivityPub.fetch_follow_information_for_user(user)
  1409. assert info.follower_count == 527
  1410. assert info.following_count == 267
  1411. end
  1412. test "detects hidden followers" do
  1413. mock(fn env ->
  1414. case env.url do
  1415. "https://remote.org/users/masto_closed/followers?page=1" ->
  1416. %Tesla.Env{status: 403, body: ""}
  1417. _ ->
  1418. apply(HttpRequestMock, :request, [env])
  1419. end
  1420. end)
  1421. user =
  1422. insert(:user,
  1423. local: false,
  1424. follower_address: "https://remote.org/users/masto_closed/followers",
  1425. following_address: "https://remote.org/users/masto_closed/following"
  1426. )
  1427. {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
  1428. assert follow_info.hide_followers == true
  1429. assert follow_info.hide_follows == false
  1430. end
  1431. test "detects hidden follows" do
  1432. mock(fn env ->
  1433. case env.url do
  1434. "https://remote.org/users/masto_closed/following?page=1" ->
  1435. %Tesla.Env{status: 403, body: ""}
  1436. _ ->
  1437. apply(HttpRequestMock, :request, [env])
  1438. end
  1439. end)
  1440. user =
  1441. insert(:user,
  1442. local: false,
  1443. follower_address: "https://remote.org/users/masto_closed/followers",
  1444. following_address: "https://remote.org/users/masto_closed/following"
  1445. )
  1446. {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
  1447. assert follow_info.hide_followers == false
  1448. assert follow_info.hide_follows == true
  1449. end
  1450. test "detects hidden follows/followers for friendica" do
  1451. user =
  1452. insert(:user,
  1453. local: false,
  1454. follower_address: "https://remote.org/followers/fuser3",
  1455. following_address: "https://remote.org/following/fuser3"
  1456. )
  1457. {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
  1458. assert follow_info.hide_followers == true
  1459. assert follow_info.follower_count == 296
  1460. assert follow_info.following_count == 32
  1461. assert follow_info.hide_follows == true
  1462. end
  1463. test "doesn't crash when follower and following counters are hidden" do
  1464. mock(fn env ->
  1465. case env.url do
  1466. "https://remote.org/users/masto_hidden_counters/following" ->
  1467. json(
  1468. %{
  1469. "@context" => "https://www.w3.org/ns/activitystreams",
  1470. "id" => "https://remote.org/users/masto_hidden_counters/followers"
  1471. },
  1472. headers: HttpRequestMock.activitypub_object_headers()
  1473. )
  1474. "https://remote.org/users/masto_hidden_counters/following?page=1" ->
  1475. %Tesla.Env{status: 403, body: ""}
  1476. "https://remote.org/users/masto_hidden_counters/followers" ->
  1477. json(
  1478. %{
  1479. "@context" => "https://www.w3.org/ns/activitystreams",
  1480. "id" => "https://remote.org/users/masto_hidden_counters/following"
  1481. },
  1482. headers: HttpRequestMock.activitypub_object_headers()
  1483. )
  1484. "https://remote.org/users/masto_hidden_counters/followers?page=1" ->
  1485. %Tesla.Env{status: 403, body: ""}
  1486. end
  1487. end)
  1488. user =
  1489. insert(:user,
  1490. local: false,
  1491. follower_address: "https://remote.org/users/masto_hidden_counters/followers",
  1492. following_address: "https://remote.org/users/masto_hidden_counters/following"
  1493. )
  1494. {:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
  1495. assert follow_info.hide_followers == true
  1496. assert follow_info.follower_count == 0
  1497. assert follow_info.hide_follows == true
  1498. assert follow_info.following_count == 0
  1499. end
  1500. end
  1501. describe "fetch_favourites/3" do
  1502. test "returns a favourite activities sorted by adds to favorite" do
  1503. user = insert(:user)
  1504. other_user = insert(:user)
  1505. user1 = insert(:user)
  1506. user2 = insert(:user)
  1507. {:ok, a1} = CommonAPI.post(user1, %{status: "bla"})
  1508. {:ok, _a2} = CommonAPI.post(user2, %{status: "traps are happy"})
  1509. {:ok, a3} = CommonAPI.post(user2, %{status: "Trees Are "})
  1510. {:ok, a4} = CommonAPI.post(user2, %{status: "Agent Smith "})
  1511. {:ok, a5} = CommonAPI.post(user1, %{status: "Red or Blue "})
  1512. {:ok, _} = CommonAPI.favorite(a4.id, user)
  1513. {:ok, _} = CommonAPI.favorite(a3.id, other_user)
  1514. {:ok, _} = CommonAPI.favorite(a3.id, user)
  1515. {:ok, _} = CommonAPI.favorite(a5.id, other_user)
  1516. {:ok, _} = CommonAPI.favorite(a5.id, user)
  1517. {:ok, _} = CommonAPI.favorite(a4.id, other_user)
  1518. {:ok, _} = CommonAPI.favorite(a1.id, user)
  1519. {:ok, _} = CommonAPI.favorite(a1.id, other_user)
  1520. result = ActivityPub.fetch_favourites(user)
  1521. assert Enum.map(result, & &1.id) == [a1.id, a5.id, a3.id, a4.id]
  1522. result = ActivityPub.fetch_favourites(user, %{limit: 2})
  1523. assert Enum.map(result, & &1.id) == [a1.id, a5.id]
  1524. end
  1525. end
  1526. describe "Move activity" do
  1527. test "create" do
  1528. %{ap_id: old_ap_id} = old_user = insert(:user)
  1529. %{ap_id: new_ap_id} = new_user = insert(:user, also_known_as: [old_ap_id])
  1530. follower = insert(:user)
  1531. follower_move_opted_out = insert(:user, allow_following_move: false)
  1532. User.follow(follower, old_user)
  1533. User.follow(follower_move_opted_out, old_user)
  1534. assert User.following?(follower, old_user)
  1535. assert User.following?(follower_move_opted_out, old_user)
  1536. assert {:ok, activity} = ActivityPub.move(old_user, new_user)
  1537. assert %Activity{
  1538. actor: ^old_ap_id,
  1539. data: %{
  1540. "actor" => ^old_ap_id,
  1541. "object" => ^old_ap_id,
  1542. "target" => ^new_ap_id,
  1543. "type" => "Move"
  1544. },
  1545. local: true,
  1546. recipients: recipients
  1547. } = activity
  1548. assert old_user.follower_address in recipients
  1549. params = %{
  1550. "op" => "move_following",
  1551. "origin_id" => old_user.id,
  1552. "target_id" => new_user.id
  1553. }
  1554. assert_enqueued(worker: Pleroma.Workers.BackgroundWorker, args: params)
  1555. Pleroma.Workers.BackgroundWorker.perform(%Oban.Job{args: params})
  1556. refute User.following?(follower, old_user)
  1557. assert User.following?(follower, new_user)
  1558. assert User.following?(follower_move_opted_out, old_user)
  1559. refute User.following?(follower_move_opted_out, new_user)
  1560. activity = %Activity{activity | object: nil}
  1561. assert [%Notification{activity: ^activity}] = Notification.for_user(follower)
  1562. assert [%Notification{activity: ^activity}] = Notification.for_user(follower_move_opted_out)
  1563. end
  1564. test "old user must be in the new user's `also_known_as` list" do
  1565. old_user = insert(:user)
  1566. new_user = insert(:user)
  1567. assert {:error, "Target account must have the origin in `alsoKnownAs`"} =
  1568. ActivityPub.move(old_user, new_user)
  1569. end
  1570. test "do not move remote user following relationships" do
  1571. %{ap_id: old_ap_id} = old_user = insert(:user)
  1572. %{ap_id: new_ap_id} = new_user = insert(:user, also_known_as: [old_ap_id])
  1573. follower_remote = insert(:user, local: false)
  1574. User.follow(follower_remote, old_user)
  1575. assert User.following?(follower_remote, old_user)
  1576. assert {:ok, activity} = ActivityPub.move(old_user, new_user)
  1577. assert %Activity{
  1578. actor: ^old_ap_id,
  1579. data: %{
  1580. "actor" => ^old_ap_id,
  1581. "object" => ^old_ap_id,
  1582. "target" => ^new_ap_id,
  1583. "type" => "Move"
  1584. },
  1585. local: true
  1586. } = activity
  1587. params = %{
  1588. "op" => "move_following",
  1589. "origin_id" => old_user.id,
  1590. "target_id" => new_user.id
  1591. }
  1592. assert_enqueued(worker: Pleroma.Workers.BackgroundWorker, args: params)
  1593. Pleroma.Workers.BackgroundWorker.perform(%Oban.Job{args: params})
  1594. assert User.following?(follower_remote, old_user)
  1595. refute User.following?(follower_remote, new_user)
  1596. end
  1597. end
  1598. test "doesn't retrieve replies activities with exclude_replies" do
  1599. user = insert(:user)
  1600. {:ok, activity} = CommonAPI.post(user, %{status: "yeah"})
  1601. {:ok, _reply} = CommonAPI.post(user, %{status: "yeah", in_reply_to_status_id: activity.id})
  1602. [result] = ActivityPub.fetch_public_activities(%{exclude_replies: true})
  1603. assert result.id == activity.id
  1604. assert length(ActivityPub.fetch_public_activities()) == 2
  1605. end
  1606. describe "replies filtering with public messages" do
  1607. setup :public_messages
  1608. test "public timeline", %{users: %{u1: user}} do
  1609. activities_ids =
  1610. %{}
  1611. |> Map.put(:type, ["Create", "Announce"])
  1612. |> Map.put(:local_only, false)
  1613. |> Map.put(:blocking_user, user)
  1614. |> Map.put(:muting_user, user)
  1615. |> Map.put(:reply_filtering_user, user)
  1616. |> ActivityPub.fetch_public_activities()
  1617. |> Enum.map(& &1.id)
  1618. assert length(activities_ids) == 16
  1619. end
  1620. test "public timeline with reply_visibility `following`", %{
  1621. users: %{u1: user},
  1622. u1: u1,
  1623. u2: u2,
  1624. u3: u3,
  1625. u4: u4,
  1626. activities: activities
  1627. } do
  1628. activities_ids =
  1629. %{}
  1630. |> Map.put(:type, ["Create", "Announce"])
  1631. |> Map.put(:local_only, false)
  1632. |> Map.put(:blocking_user, user)
  1633. |> Map.put(:muting_user, user)
  1634. |> Map.put(:reply_visibility, "following")
  1635. |> Map.put(:reply_filtering_user, user)
  1636. |> ActivityPub.fetch_public_activities()
  1637. |> Enum.map(& &1.id)
  1638. assert length(activities_ids) == 14
  1639. visible_ids =
  1640. Map.values(u1) ++ Map.values(u2) ++ Map.values(u4) ++ Map.values(activities) ++ [u3[:r1]]
  1641. assert Enum.all?(visible_ids, &(&1 in activities_ids))
  1642. end
  1643. test "public timeline with reply_visibility `self`", %{
  1644. users: %{u1: user},
  1645. u1: u1,
  1646. u2: u2,
  1647. u3: u3,
  1648. u4: u4,
  1649. activities: activities
  1650. } do
  1651. activities_ids =
  1652. %{}
  1653. |> Map.put(:type, ["Create", "Announce"])
  1654. |> Map.put(:local_only, false)
  1655. |> Map.put(:blocking_user, user)
  1656. |> Map.put(:muting_user, user)
  1657. |> Map.put(:reply_visibility, "self")
  1658. |> Map.put(:reply_filtering_user, user)
  1659. |> ActivityPub.fetch_public_activities()
  1660. |> Enum.map(& &1.id)
  1661. assert length(activities_ids) == 10
  1662. visible_ids = Map.values(u1) ++ [u2[:r1], u3[:r1], u4[:r1]] ++ Map.values(activities)
  1663. assert Enum.all?(visible_ids, &(&1 in activities_ids))
  1664. end
  1665. test "home timeline", %{
  1666. users: %{u1: user},
  1667. activities: activities,
  1668. u1: u1,
  1669. u2: u2,
  1670. u3: u3,
  1671. u4: u4
  1672. } do
  1673. params =
  1674. %{}
  1675. |> Map.put(:type, ["Create", "Announce"])
  1676. |> Map.put(:blocking_user, user)
  1677. |> Map.put(:muting_user, user)
  1678. |> Map.put(:user, user)
  1679. |> Map.put(:reply_filtering_user, user)
  1680. activities_ids =
  1681. ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
  1682. |> Enum.map(& &1.id)
  1683. assert length(activities_ids) == 13
  1684. visible_ids =
  1685. Map.values(u1) ++
  1686. Map.values(u3) ++
  1687. [
  1688. activities[:a1],
  1689. activities[:a2],
  1690. activities[:a4],
  1691. u2[:r1],
  1692. u2[:r3],
  1693. u4[:r1],
  1694. u4[:r2]
  1695. ]
  1696. assert Enum.all?(visible_ids, &(&1 in activities_ids))
  1697. end
  1698. test "home timeline with reply_visibility `following`", %{
  1699. users: %{u1: user},
  1700. activities: activities,
  1701. u1: u1,
  1702. u2: u2,
  1703. u3: u3,
  1704. u4: u4
  1705. } do
  1706. params =
  1707. %{}
  1708. |> Map.put(:type, ["Create", "Announce"])
  1709. |> Map.put(:blocking_user, user)
  1710. |> Map.put(:muting_user, user)
  1711. |> Map.put(:user, user)
  1712. |> Map.put(:reply_visibility, "following")
  1713. |> Map.put(:reply_filtering_user, user)
  1714. activities_ids =
  1715. ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
  1716. |> Enum.map(& &1.id)
  1717. assert length(activities_ids) == 11
  1718. visible_ids =
  1719. Map.values(u1) ++
  1720. [
  1721. activities[:a1],
  1722. activities[:a2],
  1723. activities[:a4],
  1724. u2[:r1],
  1725. u2[:r3],
  1726. u3[:r1],
  1727. u4[:r1],
  1728. u4[:r2]
  1729. ]
  1730. assert Enum.all?(visible_ids, &(&1 in activities_ids))
  1731. end
  1732. test "home timeline with reply_visibility `self`", %{
  1733. users: %{u1: user},
  1734. activities: activities,
  1735. u1: u1,
  1736. u2: u2,
  1737. u3: u3,
  1738. u4: u4
  1739. } do
  1740. params =
  1741. %{}
  1742. |> Map.put(:type, ["Create", "Announce"])
  1743. |> Map.put(:blocking_user, user)
  1744. |> Map.put(:muting_user, user)
  1745. |> Map.put(:user, user)
  1746. |> Map.put(:reply_visibility, "self")
  1747. |> Map.put(:reply_filtering_user, user)
  1748. activities_ids =
  1749. ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
  1750. |> Enum.map(& &1.id)
  1751. assert length(activities_ids) == 9
  1752. visible_ids =
  1753. Map.values(u1) ++
  1754. [
  1755. activities[:a1],
  1756. activities[:a2],
  1757. activities[:a4],
  1758. u2[:r1],
  1759. u3[:r1],
  1760. u4[:r1]
  1761. ]
  1762. assert Enum.all?(visible_ids, &(&1 in activities_ids))
  1763. end
  1764. test "filtering out announces where the user is the actor of the announced message" do
  1765. user = insert(:user)
  1766. other_user = insert(:user)
  1767. third_user = insert(:user)
  1768. User.follow(user, other_user)
  1769. {:ok, post} = CommonAPI.post(user, %{status: "yo"})
  1770. {:ok, other_post} = CommonAPI.post(third_user, %{status: "yo"})
  1771. {:ok, _announce} = CommonAPI.repeat(post.id, other_user)
  1772. {:ok, _announce} = CommonAPI.repeat(post.id, third_user)
  1773. {:ok, announce} = CommonAPI.repeat(other_post.id, other_user)
  1774. params = %{
  1775. type: ["Announce"]
  1776. }
  1777. results =
  1778. [user.ap_id | User.following(user)]
  1779. |> ActivityPub.fetch_activities(params)
  1780. assert length(results) == 3
  1781. params = %{
  1782. type: ["Announce"],
  1783. announce_filtering_user: user
  1784. }
  1785. [result] =
  1786. [user.ap_id | User.following(user)]
  1787. |> ActivityPub.fetch_activities(params)
  1788. assert result.id == announce.id
  1789. end
  1790. end
  1791. describe "replies filtering with private messages" do
  1792. setup :private_messages
  1793. test "public timeline", %{users: %{u1: user}} do
  1794. activities_ids =
  1795. %{}
  1796. |> Map.put(:type, ["Create", "Announce"])
  1797. |> Map.put(:local_only, false)
  1798. |> Map.put(:blocking_user, user)
  1799. |> Map.put(:muting_user, user)
  1800. |> Map.put(:user, user)
  1801. |> ActivityPub.fetch_public_activities()
  1802. |> Enum.map(& &1.id)
  1803. assert activities_ids == []
  1804. end
  1805. test "public timeline with default reply_visibility `following`", %{users: %{u1: user}} do
  1806. activities_ids =
  1807. %{}
  1808. |> Map.put(:type, ["Create", "Announce"])
  1809. |> Map.put(:local_only, false)
  1810. |> Map.put(:blocking_user, user)
  1811. |> Map.put(:muting_user, user)
  1812. |> Map.put(:reply_visibility, "following")
  1813. |> Map.put(:reply_filtering_user, user)
  1814. |> Map.put(:user, user)
  1815. |> ActivityPub.fetch_public_activities()
  1816. |> Enum.map(& &1.id)
  1817. assert activities_ids == []
  1818. end
  1819. test "public timeline with default reply_visibility `self`", %{users: %{u1: user}} do
  1820. activities_ids =
  1821. %{}
  1822. |> Map.put(:type, ["Create", "Announce"])
  1823. |> Map.put(:local_only, false)
  1824. |> Map.put(:blocking_user, user)
  1825. |> Map.put(:muting_user, user)
  1826. |> Map.put(:reply_visibility, "self")
  1827. |> Map.put(:reply_filtering_user, user)
  1828. |> Map.put(:user, user)
  1829. |> ActivityPub.fetch_public_activities()
  1830. |> Enum.map(& &1.id)
  1831. assert activities_ids == []
  1832. activities_ids =
  1833. %{}
  1834. |> Map.put(:reply_visibility, "self")
  1835. |> Map.put(:reply_filtering_user, nil)
  1836. |> ActivityPub.fetch_public_activities()
  1837. assert activities_ids == []
  1838. end
  1839. test "home timeline", %{users: %{u1: user}} do
  1840. params =
  1841. %{}
  1842. |> Map.put(:type, ["Create", "Announce"])
  1843. |> Map.put(:blocking_user, user)
  1844. |> Map.put(:muting_user, user)
  1845. |> Map.put(:user, user)
  1846. activities_ids =
  1847. ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
  1848. |> Enum.map(& &1.id)
  1849. assert length(activities_ids) == 12
  1850. end
  1851. test "home timeline with default reply_visibility `following`", %{users: %{u1: user}} do
  1852. params =
  1853. %{}
  1854. |> Map.put(:type, ["Create", "Announce"])
  1855. |> Map.put(:blocking_user, user)
  1856. |> Map.put(:muting_user, user)
  1857. |> Map.put(:user, user)
  1858. |> Map.put(:reply_visibility, "following")
  1859. |> Map.put(:reply_filtering_user, user)
  1860. activities_ids =
  1861. ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
  1862. |> Enum.map(& &1.id)
  1863. assert length(activities_ids) == 12
  1864. end
  1865. test "home timeline with default reply_visibility `self`", %{
  1866. users: %{u1: user},
  1867. activities: activities,
  1868. u1: u1,
  1869. u2: u2,
  1870. u3: u3,
  1871. u4: u4
  1872. } do
  1873. params =
  1874. %{}
  1875. |> Map.put(:type, ["Create", "Announce"])
  1876. |> Map.put(:blocking_user, user)
  1877. |> Map.put(:muting_user, user)
  1878. |> Map.put(:user, user)
  1879. |> Map.put(:reply_visibility, "self")
  1880. |> Map.put(:reply_filtering_user, user)
  1881. activities_ids =
  1882. ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
  1883. |> Enum.map(& &1.id)
  1884. assert length(activities_ids) == 10
  1885. visible_ids =
  1886. Map.values(u1) ++ Map.values(u4) ++ [u2[:r1], u3[:r1]] ++ Map.values(activities)
  1887. assert Enum.all?(visible_ids, &(&1 in activities_ids))
  1888. end
  1889. end
  1890. defp public_messages(_) do
  1891. [u1, u2, u3, u4] = insert_list(4, :user)
  1892. {:ok, u1, u2} = User.follow(u1, u2)
  1893. {:ok, u2, u1} = User.follow(u2, u1)
  1894. {:ok, u1, u4} = User.follow(u1, u4)
  1895. {:ok, u4, u1} = User.follow(u4, u1)
  1896. {:ok, u2, u3} = User.follow(u2, u3)
  1897. {:ok, u3, u2} = User.follow(u3, u2)
  1898. {:ok, a1} = CommonAPI.post(u1, %{status: "Status"})
  1899. {:ok, r1_1} =
  1900. CommonAPI.post(u2, %{
  1901. status: "@#{u1.nickname} reply from u2 to u1",
  1902. in_reply_to_status_id: a1.id
  1903. })
  1904. {:ok, r1_2} =
  1905. CommonAPI.post(u3, %{
  1906. status: "@#{u1.nickname} reply from u3 to u1",
  1907. in_reply_to_status_id: a1.id
  1908. })
  1909. {:ok, r1_3} =
  1910. CommonAPI.post(u4, %{
  1911. status: "@#{u1.nickname} reply from u4 to u1",
  1912. in_reply_to_status_id: a1.id
  1913. })
  1914. {:ok, a2} = CommonAPI.post(u2, %{status: "Status"})
  1915. {:ok, r2_1} =
  1916. CommonAPI.post(u1, %{
  1917. status: "@#{u2.nickname} reply from u1 to u2",
  1918. in_reply_to_status_id: a2.id
  1919. })
  1920. {:ok, r2_2} =
  1921. CommonAPI.post(u3, %{
  1922. status: "@#{u2.nickname} reply from u3 to u2",
  1923. in_reply_to_status_id: a2.id
  1924. })
  1925. {:ok, r2_3} =
  1926. CommonAPI.post(u4, %{
  1927. status: "@#{u2.nickname} reply from u4 to u2",
  1928. in_reply_to_status_id: a2.id
  1929. })
  1930. {:ok, a3} = CommonAPI.post(u3, %{status: "Status"})
  1931. {:ok, r3_1} =
  1932. CommonAPI.post(u1, %{
  1933. status: "@#{u3.nickname} reply from u1 to u3",
  1934. in_reply_to_status_id: a3.id
  1935. })
  1936. {:ok, r3_2} =
  1937. CommonAPI.post(u2, %{
  1938. status: "@#{u3.nickname} reply from u2 to u3",
  1939. in_reply_to_status_id: a3.id
  1940. })
  1941. {:ok, r3_3} =
  1942. CommonAPI.post(u4, %{
  1943. status: "@#{u3.nickname} reply from u4 to u3",
  1944. in_reply_to_status_id: a3.id
  1945. })
  1946. {:ok, a4} = CommonAPI.post(u4, %{status: "Status"})
  1947. {:ok, r4_1} =
  1948. CommonAPI.post(u1, %{
  1949. status: "@#{u4.nickname} reply from u1 to u4",
  1950. in_reply_to_status_id: a4.id
  1951. })
  1952. {:ok, r4_2} =
  1953. CommonAPI.post(u2, %{
  1954. status: "@#{u4.nickname} reply from u2 to u4",
  1955. in_reply_to_status_id: a4.id
  1956. })
  1957. {:ok, r4_3} =
  1958. CommonAPI.post(u3, %{
  1959. status: "@#{u4.nickname} reply from u3 to u4",
  1960. in_reply_to_status_id: a4.id
  1961. })
  1962. {:ok,
  1963. users: %{u1: u1, u2: u2, u3: u3, u4: u4},
  1964. activities: %{a1: a1.id, a2: a2.id, a3: a3.id, a4: a4.id},
  1965. u1: %{r1: r1_1.id, r2: r1_2.id, r3: r1_3.id},
  1966. u2: %{r1: r2_1.id, r2: r2_2.id, r3: r2_3.id},
  1967. u3: %{r1: r3_1.id, r2: r3_2.id, r3: r3_3.id},
  1968. u4: %{r1: r4_1.id, r2: r4_2.id, r3: r4_3.id}}
  1969. end
  1970. defp private_messages(_) do
  1971. [u1, u2, u3, u4] = insert_list(4, :user)
  1972. {:ok, u1, u2} = User.follow(u1, u2)
  1973. {:ok, u2, u1} = User.follow(u2, u1)
  1974. {:ok, u1, u3} = User.follow(u1, u3)
  1975. {:ok, u3, u1} = User.follow(u3, u1)
  1976. {:ok, u1, u4} = User.follow(u1, u4)
  1977. {:ok, u4, u1} = User.follow(u4, u1)
  1978. {:ok, u2, u3} = User.follow(u2, u3)
  1979. {:ok, u3, u2} = User.follow(u3, u2)
  1980. {:ok, a1} = CommonAPI.post(u1, %{status: "Status", visibility: "private"})
  1981. {:ok, r1_1} =
  1982. CommonAPI.post(u2, %{
  1983. status: "@#{u1.nickname} reply from u2 to u1",
  1984. in_reply_to_status_id: a1.id,
  1985. visibility: "private"
  1986. })
  1987. {:ok, r1_2} =
  1988. CommonAPI.post(u3, %{
  1989. status: "@#{u1.nickname} reply from u3 to u1",
  1990. in_reply_to_status_id: a1.id,
  1991. visibility: "private"
  1992. })
  1993. {:ok, r1_3} =
  1994. CommonAPI.post(u4, %{
  1995. status: "@#{u1.nickname} reply from u4 to u1",
  1996. in_reply_to_status_id: a1.id,
  1997. visibility: "private"
  1998. })
  1999. {:ok, a2} = CommonAPI.post(u2, %{status: "Status", visibility: "private"})
  2000. {:ok, r2_1} =
  2001. CommonAPI.post(u1, %{
  2002. status: "@#{u2.nickname} reply from u1 to u2",
  2003. in_reply_to_status_id: a2.id,
  2004. visibility: "private"
  2005. })
  2006. {:ok, r2_2} =
  2007. CommonAPI.post(u3, %{
  2008. status: "@#{u2.nickname} reply from u3 to u2",
  2009. in_reply_to_status_id: a2.id,
  2010. visibility: "private"
  2011. })
  2012. {:ok, a3} = CommonAPI.post(u3, %{status: "Status", visibility: "private"})
  2013. {:ok, r3_1} =
  2014. CommonAPI.post(u1, %{
  2015. status: "@#{u3.nickname} reply from u1 to u3",
  2016. in_reply_to_status_id: a3.id,
  2017. visibility: "private"
  2018. })
  2019. {:ok, r3_2} =
  2020. CommonAPI.post(u2, %{
  2021. status: "@#{u3.nickname} reply from u2 to u3",
  2022. in_reply_to_status_id: a3.id,
  2023. visibility: "private"
  2024. })
  2025. {:ok, a4} = CommonAPI.post(u4, %{status: "Status", visibility: "private"})
  2026. {:ok, r4_1} =
  2027. CommonAPI.post(u1, %{
  2028. status: "@#{u4.nickname} reply from u1 to u4",
  2029. in_reply_to_status_id: a4.id,
  2030. visibility: "private"
  2031. })
  2032. {:ok,
  2033. users: %{u1: u1, u2: u2, u3: u3, u4: u4},
  2034. activities: %{a1: a1.id, a2: a2.id, a3: a3.id, a4: a4.id},
  2035. u1: %{r1: r1_1.id, r2: r1_2.id, r3: r1_3.id},
  2036. u2: %{r1: r2_1.id, r2: r2_2.id},
  2037. u3: %{r1: r3_1.id, r2: r3_2.id},
  2038. u4: %{r1: r4_1.id}}
  2039. end
  2040. describe "maybe_update_follow_information/1" do
  2041. setup do
  2042. clear_config([:instance, :external_user_synchronization], true)
  2043. user = %{
  2044. local: false,
  2045. ap_id: "https://gensokyo.2hu/users/raymoo",
  2046. following_address: "https://gensokyo.2hu/users/following",
  2047. follower_address: "https://gensokyo.2hu/users/followers",
  2048. type: "Person"
  2049. }
  2050. %{user: user}
  2051. end
  2052. test "logs an error when it can't fetch the info", %{user: user} do
  2053. assert capture_log(fn ->
  2054. ActivityPub.maybe_update_follow_information(user)
  2055. end) =~ "Follower/Following counter update for #{user.ap_id} failed"
  2056. end
  2057. test "just returns the input if the user type is Application", %{
  2058. user: user
  2059. } do
  2060. user =
  2061. user
  2062. |> Map.put(:type, "Application")
  2063. refute capture_log(fn ->
  2064. assert ^user = ActivityPub.maybe_update_follow_information(user)
  2065. end) =~ "Follower/Following counter update for #{user.ap_id} failed"
  2066. end
  2067. test "it just returns the input if the user has no following/follower addresses", %{
  2068. user: user
  2069. } do
  2070. user =
  2071. user
  2072. |> Map.put(:following_address, nil)
  2073. |> Map.put(:follower_address, nil)
  2074. refute capture_log(fn ->
  2075. assert ^user = ActivityPub.maybe_update_follow_information(user)
  2076. end) =~ "Follower/Following counter update for #{user.ap_id} failed"
  2077. end
  2078. end
  2079. describe "global activity expiration" do
  2080. test "creates an activity expiration for local Create activities" do
  2081. clear_config([:mrf, :policies], Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy)
  2082. {:ok, activity} = ActivityBuilder.insert(%{"type" => "Create", "context" => "3hu"})
  2083. {:ok, follow} = ActivityBuilder.insert(%{"type" => "Follow", "context" => "3hu"})
  2084. assert_enqueued(
  2085. worker: Pleroma.Workers.PurgeExpiredActivity,
  2086. args: %{activity_id: activity.id},
  2087. scheduled_at:
  2088. activity.inserted_at
  2089. |> DateTime.from_naive!("Etc/UTC")
  2090. |> Timex.shift(days: 365)
  2091. )
  2092. refute_enqueued(
  2093. worker: Pleroma.Workers.PurgeExpiredActivity,
  2094. args: %{activity_id: follow.id}
  2095. )
  2096. end
  2097. end
  2098. describe "handling of clashing nicknames" do
  2099. test "renames an existing user with a clashing nickname and a different ap id" do
  2100. orig_user =
  2101. insert(
  2102. :user,
  2103. local: false,
  2104. nickname: "admin@mastodon.example.org",
  2105. ap_id: "http://mastodon.example.org/users/harinezumigari"
  2106. )
  2107. %{
  2108. nickname: orig_user.nickname,
  2109. ap_id: orig_user.ap_id <> "part_2"
  2110. }
  2111. |> ActivityPub.maybe_handle_clashing_nickname()
  2112. user = User.get_by_id(orig_user.id)
  2113. assert user.nickname == "#{orig_user.id}.admin@mastodon.example.org"
  2114. end
  2115. test "does nothing with a clashing nickname and the same ap id" do
  2116. orig_user =
  2117. insert(
  2118. :user,
  2119. local: false,
  2120. nickname: "admin@mastodon.example.org",
  2121. ap_id: "http://mastodon.example.org/users/harinezumigari"
  2122. )
  2123. %{
  2124. nickname: orig_user.nickname,
  2125. ap_id: orig_user.ap_id
  2126. }
  2127. |> ActivityPub.maybe_handle_clashing_nickname()
  2128. user = User.get_by_id(orig_user.id)
  2129. assert user.nickname == orig_user.nickname
  2130. end
  2131. end
  2132. describe "reply filtering" do
  2133. test "`following` still contains announcements by friends" do
  2134. user = insert(:user)
  2135. followed = insert(:user)
  2136. not_followed = insert(:user)
  2137. User.follow(user, followed)
  2138. {:ok, followed_post} = CommonAPI.post(followed, %{status: "Hello"})
  2139. {:ok, not_followed_to_followed} =
  2140. CommonAPI.post(not_followed, %{
  2141. status: "Also hello",
  2142. in_reply_to_status_id: followed_post.id
  2143. })
  2144. {:ok, retoot} = CommonAPI.repeat(not_followed_to_followed.id, followed)
  2145. params =
  2146. %{}
  2147. |> Map.put(:type, ["Create", "Announce"])
  2148. |> Map.put(:blocking_user, user)
  2149. |> Map.put(:muting_user, user)
  2150. |> Map.put(:reply_filtering_user, user)
  2151. |> Map.put(:reply_visibility, "following")
  2152. |> Map.put(:announce_filtering_user, user)
  2153. |> Map.put(:user, user)
  2154. activities =
  2155. [user.ap_id | User.following(user)]
  2156. |> ActivityPub.fetch_activities(params)
  2157. followed_post_id = followed_post.id
  2158. retoot_id = retoot.id
  2159. assert [%{id: ^followed_post_id}, %{id: ^retoot_id}] = activities
  2160. assert length(activities) == 2
  2161. end
  2162. # This test is skipped because, while this is the desired behavior,
  2163. # there seems to be no good way to achieve it with the method that
  2164. # we currently use for detecting to who a reply is directed.
  2165. # This is a TODO and should be fixed by a later rewrite of the code
  2166. # in question.
  2167. @tag skip: true
  2168. test "`following` still contains self-replies by friends" do
  2169. user = insert(:user)
  2170. followed = insert(:user)
  2171. not_followed = insert(:user)
  2172. User.follow(user, followed)
  2173. {:ok, followed_post} = CommonAPI.post(followed, %{status: "Hello"})
  2174. {:ok, not_followed_post} = CommonAPI.post(not_followed, %{status: "Also hello"})
  2175. {:ok, _followed_to_not_followed} =
  2176. CommonAPI.post(followed, %{status: "sup", in_reply_to_status_id: not_followed_post.id})
  2177. {:ok, _followed_self_reply} =
  2178. CommonAPI.post(followed, %{status: "Also cofe", in_reply_to_status_id: followed_post.id})
  2179. params =
  2180. %{}
  2181. |> Map.put(:type, ["Create", "Announce"])
  2182. |> Map.put(:blocking_user, user)
  2183. |> Map.put(:muting_user, user)
  2184. |> Map.put(:reply_filtering_user, user)
  2185. |> Map.put(:reply_visibility, "following")
  2186. |> Map.put(:announce_filtering_user, user)
  2187. |> Map.put(:user, user)
  2188. activities =
  2189. [user.ap_id | User.following(user)]
  2190. |> ActivityPub.fetch_activities(params)
  2191. assert length(activities) == 2
  2192. end
  2193. end
  2194. test "allow fetching of accounts with an empty string name field" do
  2195. Tesla.Mock.mock(fn
  2196. %{method: :get, url: "https://princess.cat/users/mewmew"} ->
  2197. file = File.read!("test/fixtures/mewmew_no_name.json")
  2198. %Tesla.Env{status: 200, body: file, headers: HttpRequestMock.activitypub_object_headers()}
  2199. end)
  2200. {:ok, user} = ActivityPub.make_user_from_ap_id("https://princess.cat/users/mewmew")
  2201. assert user.name == " "
  2202. end
  2203. test "pin_data_from_featured_collection will ignore unsupported values" do
  2204. assert %{} ==
  2205. ActivityPub.pin_data_from_featured_collection(%{
  2206. "type" => "OrderedCollection",
  2207. "first" => "https://social.example/users/alice/collections/featured?page=true"
  2208. })
  2209. end
  2210. end