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 (89072B)


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