logo

pleroma

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

transmogrifier_test.exs (31374B)


  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.TransmogrifierTest do
  5. use Oban.Testing, repo: Pleroma.Repo
  6. use Pleroma.DataCase
  7. alias Pleroma.Activity
  8. alias Pleroma.Object
  9. alias Pleroma.User
  10. alias Pleroma.Web.ActivityPub.Transmogrifier
  11. alias Pleroma.Web.ActivityPub.Utils
  12. alias Pleroma.Web.AdminAPI.AccountView
  13. alias Pleroma.Web.CommonAPI
  14. import Mock
  15. import Pleroma.Factory
  16. import ExUnit.CaptureLog
  17. setup_all do
  18. Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
  19. :ok
  20. end
  21. setup do: clear_config([:instance, :max_remote_account_fields])
  22. describe "handle_incoming" do
  23. test "it works for incoming unfollows with an existing follow" do
  24. user = insert(:user)
  25. follow_data =
  26. File.read!("test/fixtures/mastodon-follow-activity.json")
  27. |> Jason.decode!()
  28. |> Map.put("object", user.ap_id)
  29. {:ok, %Activity{data: _, local: false}} = Transmogrifier.handle_incoming(follow_data)
  30. data =
  31. File.read!("test/fixtures/mastodon-unfollow-activity.json")
  32. |> Jason.decode!()
  33. |> Map.put("object", follow_data)
  34. {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
  35. assert data["type"] == "Undo"
  36. assert data["object"]["type"] == "Follow"
  37. assert data["object"]["object"] == user.ap_id
  38. assert data["actor"] == "http://mastodon.example.org/users/admin"
  39. refute User.following?(User.get_cached_by_ap_id(data["actor"]), user)
  40. end
  41. test "it accepts Flag activities" do
  42. user = insert(:user)
  43. other_user = insert(:user)
  44. {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
  45. object = Object.normalize(activity, fetch: false)
  46. note_obj = %{
  47. "type" => "Note",
  48. "id" => activity.object.data["id"],
  49. "content" => "test post",
  50. "published" => object.data["published"],
  51. "actor" => AccountView.render("show.json", %{user: user, skip_visibility_check: true})
  52. }
  53. message = %{
  54. "@context" => "https://www.w3.org/ns/activitystreams",
  55. "cc" => [user.ap_id],
  56. "object" => [user.ap_id, activity.data["id"]],
  57. "type" => "Flag",
  58. "content" => "blocked AND reported!!!",
  59. "actor" => other_user.ap_id
  60. }
  61. assert {:ok, activity} = Transmogrifier.handle_incoming(message)
  62. assert activity.data["object"] == [user.ap_id, note_obj]
  63. assert activity.data["content"] == "blocked AND reported!!!"
  64. assert activity.data["actor"] == other_user.ap_id
  65. assert activity.data["cc"] == [user.ap_id]
  66. end
  67. test "it accepts Move activities" do
  68. old_user = insert(:user)
  69. new_user = insert(:user)
  70. message = %{
  71. "@context" => "https://www.w3.org/ns/activitystreams",
  72. "type" => "Move",
  73. "actor" => old_user.ap_id,
  74. "object" => old_user.ap_id,
  75. "target" => new_user.ap_id
  76. }
  77. assert :error = Transmogrifier.handle_incoming(message)
  78. {:ok, _new_user} = User.update_and_set_cache(new_user, %{also_known_as: [old_user.ap_id]})
  79. assert {:ok, %Activity{} = activity} = Transmogrifier.handle_incoming(message)
  80. assert activity.actor == old_user.ap_id
  81. assert activity.data["actor"] == old_user.ap_id
  82. assert activity.data["object"] == old_user.ap_id
  83. assert activity.data["target"] == new_user.ap_id
  84. assert activity.data["type"] == "Move"
  85. end
  86. test "it fixes both the Create and object contexts in a reply" do
  87. insert(:user, ap_id: "https://mk.absturztau.be/users/8ozbzjs3o8")
  88. insert(:user, ap_id: "https://p.helene.moe/users/helene")
  89. create_activity =
  90. "test/fixtures/create-pleroma-reply-to-misskey-thread.json"
  91. |> File.read!()
  92. |> Jason.decode!()
  93. assert {:ok, %Activity{} = activity} = Transmogrifier.handle_incoming(create_activity)
  94. object = Object.normalize(activity, fetch: false)
  95. assert activity.data["context"] == object.data["context"]
  96. end
  97. test "it keeps link tags" do
  98. insert(:user, ap_id: "https://example.org/users/alice")
  99. message = File.read!("test/fixtures/fep-e232.json") |> Jason.decode!()
  100. assert capture_log(fn ->
  101. assert {:ok, activity} = Transmogrifier.handle_incoming(message)
  102. object = Object.normalize(activity)
  103. assert [%{"type" => "Mention"}, %{"type" => "Link"}] = object.data["tag"]
  104. end) =~ "Object rejected while fetching"
  105. end
  106. test "it accepts quote posts" do
  107. insert(:user, ap_id: "https://misskey.io/users/7rkrarq81i")
  108. object = File.read!("test/fixtures/quote_post/misskey_quote_post.json") |> Jason.decode!()
  109. message = %{
  110. "@context" => "https://www.w3.org/ns/activitystreams",
  111. "type" => "Create",
  112. "actor" => "https://misskey.io/users/7rkrarq81i",
  113. "object" => object
  114. }
  115. assert {:ok, activity} = Transmogrifier.handle_incoming(message)
  116. # Object was created in the database
  117. object = Object.normalize(activity)
  118. assert object.data["quoteUrl"] == "https://misskey.io/notes/8vs6wxufd0"
  119. # It fetched the quoted post
  120. assert Object.normalize("https://misskey.io/notes/8vs6wxufd0")
  121. end
  122. test "doesn't allow remote edits to fake local likes" do
  123. # as a spot check for no internal fields getting injected
  124. now = DateTime.utc_now()
  125. pub_date = DateTime.to_iso8601(Timex.subtract(now, Timex.Duration.from_minutes(3)))
  126. edit_date = DateTime.to_iso8601(now)
  127. local_user = insert(:user)
  128. create_data = %{
  129. "type" => "Create",
  130. "id" => "http://mastodon.example.org/users/admin/statuses/2619539638/activity",
  131. "actor" => "http://mastodon.example.org/users/admin",
  132. "to" => ["https://www.w3.org/ns/activitystreams#Public"],
  133. "cc" => [],
  134. "object" => %{
  135. "type" => "Note",
  136. "id" => "http://mastodon.example.org/users/admin/statuses/2619539638",
  137. "attributedTo" => "http://mastodon.example.org/users/admin",
  138. "to" => ["https://www.w3.org/ns/activitystreams#Public"],
  139. "cc" => [],
  140. "published" => pub_date,
  141. "content" => "miaow",
  142. "likes" => [local_user.ap_id]
  143. }
  144. }
  145. update_data =
  146. create_data
  147. |> Map.put("type", "Update")
  148. |> Map.put("id", create_data["object"]["id"] <> "/update/1")
  149. |> put_in(["object", "content"], "miaow :3")
  150. |> put_in(["object", "updated"], edit_date)
  151. |> put_in(["object", "formerRepresentations"], %{
  152. "type" => "OrderedCollection",
  153. "totalItems" => 1,
  154. "orderedItems" => [create_data["object"]]
  155. })
  156. {:ok, %Pleroma.Activity{} = activity} = Transmogrifier.handle_incoming(create_data)
  157. %Pleroma.Object{} = object = Object.get_by_ap_id(activity.data["object"])
  158. assert object.data["content"] == "miaow"
  159. assert object.data["likes"] == []
  160. assert object.data["like_count"] == 0
  161. {:ok, %Pleroma.Activity{} = activity} = Transmogrifier.handle_incoming(update_data)
  162. %Pleroma.Object{} = object = Object.get_by_ap_id(activity.data["object"]["id"])
  163. assert object.data["content"] == "miaow :3"
  164. assert object.data["likes"] == []
  165. assert object.data["like_count"] == 0
  166. end
  167. test "strips internal fields from history items in edited notes" do
  168. now = DateTime.utc_now()
  169. pub_date = DateTime.to_iso8601(Timex.subtract(now, Timex.Duration.from_minutes(3)))
  170. edit_date = DateTime.to_iso8601(now)
  171. local_user = insert(:user)
  172. create_data = %{
  173. "type" => "Create",
  174. "id" => "http://mastodon.example.org/users/admin/statuses/2619539638/activity",
  175. "actor" => "http://mastodon.example.org/users/admin",
  176. "to" => ["https://www.w3.org/ns/activitystreams#Public"],
  177. "cc" => [],
  178. "object" => %{
  179. "type" => "Note",
  180. "id" => "http://mastodon.example.org/users/admin/statuses/2619539638",
  181. "attributedTo" => "http://mastodon.example.org/users/admin",
  182. "to" => ["https://www.w3.org/ns/activitystreams#Public"],
  183. "cc" => [],
  184. "published" => pub_date,
  185. "content" => "miaow",
  186. "likes" => [],
  187. "like_count" => 0
  188. }
  189. }
  190. update_data =
  191. create_data
  192. |> Map.put("type", "Update")
  193. |> Map.put("id", create_data["object"]["id"] <> "/update/1")
  194. |> put_in(["object", "content"], "miaow :3")
  195. |> put_in(["object", "updated"], edit_date)
  196. |> put_in(["object", "formerRepresentations"], %{
  197. "type" => "OrderedCollection",
  198. "totalItems" => 1,
  199. "orderedItems" => [
  200. Map.merge(create_data["object"], %{
  201. "likes" => [local_user.ap_id],
  202. "like_count" => 1,
  203. "pleroma" => %{"internal_field" => "should_be_stripped"}
  204. })
  205. ]
  206. })
  207. {:ok, %Pleroma.Activity{} = activity} = Transmogrifier.handle_incoming(create_data)
  208. %Pleroma.Object{} = object = Object.get_by_ap_id(activity.data["object"])
  209. assert object.data["content"] == "miaow"
  210. assert object.data["likes"] == []
  211. assert object.data["like_count"] == 0
  212. {:ok, %Pleroma.Activity{} = activity} = Transmogrifier.handle_incoming(update_data)
  213. %Pleroma.Object{} = object = Object.get_by_ap_id(activity.data["object"]["id"])
  214. assert object.data["content"] == "miaow :3"
  215. assert object.data["likes"] == []
  216. assert object.data["like_count"] == 0
  217. # Check that internal fields are stripped from history items
  218. history_item = List.first(object.data["formerRepresentations"]["orderedItems"])
  219. assert history_item["likes"] == []
  220. assert history_item["like_count"] == 0
  221. refute Map.has_key?(history_item, "pleroma")
  222. end
  223. test "doesn't trip over remote likes in notes" do
  224. now = DateTime.utc_now()
  225. pub_date = DateTime.to_iso8601(Timex.subtract(now, Timex.Duration.from_minutes(3)))
  226. edit_date = DateTime.to_iso8601(now)
  227. create_data = %{
  228. "type" => "Create",
  229. "id" => "http://mastodon.example.org/users/admin/statuses/3409297097/activity",
  230. "actor" => "http://mastodon.example.org/users/admin",
  231. "to" => ["https://www.w3.org/ns/activitystreams#Public"],
  232. "cc" => [],
  233. "object" => %{
  234. "type" => "Note",
  235. "id" => "http://mastodon.example.org/users/admin/statuses/3409297097",
  236. "attributedTo" => "http://mastodon.example.org/users/admin",
  237. "to" => ["https://www.w3.org/ns/activitystreams#Public"],
  238. "cc" => [],
  239. "published" => pub_date,
  240. "content" => "miaow",
  241. "likes" => %{
  242. "id" => "http://mastodon.example.org/users/admin/statuses/3409297097/likes",
  243. "totalItems" => 0,
  244. "type" => "Collection"
  245. }
  246. }
  247. }
  248. update_data =
  249. create_data
  250. |> Map.put("type", "Update")
  251. |> Map.put("id", create_data["object"]["id"] <> "/update/1")
  252. |> put_in(["object", "content"], "miaow :3")
  253. |> put_in(["object", "updated"], edit_date)
  254. |> put_in(["object", "likes", "totalItems"], 666)
  255. |> put_in(["object", "formerRepresentations"], %{
  256. "type" => "OrderedCollection",
  257. "totalItems" => 1,
  258. "orderedItems" => [create_data["object"]]
  259. })
  260. {:ok, %Pleroma.Activity{} = activity} = Transmogrifier.handle_incoming(create_data)
  261. %Pleroma.Object{} = object = Object.get_by_ap_id(activity.data["object"])
  262. assert object.data["content"] == "miaow"
  263. assert object.data["likes"] == []
  264. assert object.data["like_count"] == 0
  265. {:ok, %Pleroma.Activity{} = activity} = Transmogrifier.handle_incoming(update_data)
  266. %Pleroma.Object{} = object = Object.get_by_ap_id(activity.data["object"]["id"])
  267. assert object.data["content"] == "miaow :3"
  268. assert object.data["likes"] == []
  269. # in the future this should retain remote likes, but for now:
  270. assert object.data["like_count"] == 0
  271. end
  272. test "doesn't trip over remote likes in polls" do
  273. now = DateTime.utc_now()
  274. pub_date = DateTime.to_iso8601(Timex.subtract(now, Timex.Duration.from_minutes(3)))
  275. edit_date = DateTime.to_iso8601(now)
  276. create_data = %{
  277. "type" => "Create",
  278. "id" => "http://mastodon.example.org/users/admin/statuses/2471790073/activity",
  279. "actor" => "http://mastodon.example.org/users/admin",
  280. "to" => ["https://www.w3.org/ns/activitystreams#Public"],
  281. "cc" => [],
  282. "object" => %{
  283. "type" => "Question",
  284. "id" => "http://mastodon.example.org/users/admin/statuses/2471790073",
  285. "attributedTo" => "http://mastodon.example.org/users/admin",
  286. "to" => ["https://www.w3.org/ns/activitystreams#Public"],
  287. "cc" => [],
  288. "published" => pub_date,
  289. "content" => "vote!",
  290. "anyOf" => [
  291. %{
  292. "type" => "Note",
  293. "name" => "a",
  294. "replies" => %{
  295. "type" => "Collection",
  296. "totalItems" => 3
  297. }
  298. },
  299. %{
  300. "type" => "Note",
  301. "name" => "b",
  302. "replies" => %{
  303. "type" => "Collection",
  304. "totalItems" => 1
  305. }
  306. }
  307. ],
  308. "likes" => %{
  309. "id" => "http://mastodon.example.org/users/admin/statuses/2471790073/likes",
  310. "totalItems" => 0,
  311. "type" => "Collection"
  312. }
  313. }
  314. }
  315. update_data =
  316. create_data
  317. |> Map.put("type", "Update")
  318. |> Map.put("id", create_data["object"]["id"] <> "/update/1")
  319. |> put_in(["object", "content"], "vote now!")
  320. |> put_in(["object", "updated"], edit_date)
  321. |> put_in(["object", "likes", "totalItems"], 666)
  322. |> put_in(["object", "formerRepresentations"], %{
  323. "type" => "OrderedCollection",
  324. "totalItems" => 1,
  325. "orderedItems" => [create_data["object"]]
  326. })
  327. {:ok, %Pleroma.Activity{} = activity} = Transmogrifier.handle_incoming(create_data)
  328. %Pleroma.Object{} = object = Object.get_by_ap_id(activity.data["object"])
  329. assert object.data["content"] == "vote!"
  330. assert object.data["likes"] == []
  331. assert object.data["like_count"] == 0
  332. {:ok, %Pleroma.Activity{} = activity} = Transmogrifier.handle_incoming(update_data)
  333. %Pleroma.Object{} = object = Object.get_by_ap_id(activity.data["object"]["id"])
  334. assert object.data["content"] == "vote now!"
  335. assert object.data["likes"] == []
  336. # in the future this should retain remote likes, but for now:
  337. assert object.data["like_count"] == 0
  338. end
  339. end
  340. describe "prepare outgoing" do
  341. test "it inlines private announced objects" do
  342. user = insert(:user)
  343. {:ok, activity} = CommonAPI.post(user, %{status: "hey", visibility: "private"})
  344. {:ok, announce_activity} = CommonAPI.repeat(activity.id, user)
  345. {:ok, modified} = Transmogrifier.prepare_outgoing(announce_activity.data)
  346. assert modified["object"]["content"] == "hey"
  347. assert modified["object"]["actor"] == modified["object"]["attributedTo"]
  348. end
  349. test "it turns mentions into tags" do
  350. user = insert(:user)
  351. other_user = insert(:user)
  352. {:ok, activity} =
  353. CommonAPI.post(user, %{status: "hey, @#{other_user.nickname}, how are ya? #2hu"})
  354. with_mock Pleroma.Notification,
  355. get_notified_from_activity: fn _, _ -> [] end do
  356. {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
  357. object = modified["object"]
  358. expected_mention = %{
  359. "href" => other_user.ap_id,
  360. "name" => "@#{other_user.nickname}",
  361. "type" => "Mention"
  362. }
  363. expected_tag = %{
  364. "href" => Pleroma.Web.Endpoint.url() <> "/tags/2hu",
  365. "type" => "Hashtag",
  366. "name" => "#2hu"
  367. }
  368. refute called(Pleroma.Notification.get_notified_from_activity(:_, :_))
  369. assert Enum.member?(object["tag"], expected_tag)
  370. assert Enum.member?(object["tag"], expected_mention)
  371. end
  372. end
  373. test "it adds the json-ld context and the conversation property" do
  374. user = insert(:user)
  375. {:ok, activity} = CommonAPI.post(user, %{status: "hey"})
  376. {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
  377. assert modified["@context"] == Utils.make_json_ld_header()["@context"]
  378. assert modified["object"]["conversation"] == modified["context"]
  379. end
  380. test "it sets the 'attributedTo' property to the actor of the object if it doesn't have one" do
  381. user = insert(:user)
  382. {:ok, activity} = CommonAPI.post(user, %{status: "hey"})
  383. {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
  384. assert modified["object"]["actor"] == modified["object"]["attributedTo"]
  385. end
  386. test "it strips internal hashtag data" do
  387. user = insert(:user)
  388. {:ok, activity} = CommonAPI.post(user, %{status: "#2hu"})
  389. expected_tag = %{
  390. "href" => Pleroma.Web.Endpoint.url() <> "/tags/2hu",
  391. "type" => "Hashtag",
  392. "name" => "#2hu"
  393. }
  394. {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
  395. assert modified["object"]["tag"] == [expected_tag]
  396. end
  397. test "it strips internal fields" do
  398. user = insert(:user)
  399. {:ok, activity} =
  400. CommonAPI.post(user, %{
  401. status: "#2hu :firefox:",
  402. generator: %{type: "Application", name: "TestClient", url: "https://pleroma.social"}
  403. })
  404. # Ensure injected application data made it into the activity
  405. # as we don't have a Token to derive it from, otherwise it will
  406. # be nil and the test will pass
  407. assert %{
  408. type: "Application",
  409. name: "TestClient",
  410. url: "https://pleroma.social"
  411. } == activity.object.data["generator"]
  412. {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
  413. assert length(modified["object"]["tag"]) == 2
  414. assert is_nil(modified["object"]["emoji"])
  415. assert is_nil(modified["object"]["like_count"])
  416. assert is_nil(modified["object"]["announcements"])
  417. assert is_nil(modified["object"]["announcement_count"])
  418. assert is_nil(modified["object"]["generator"])
  419. end
  420. test "it strips internal fields of article" do
  421. activity = insert(:article_activity)
  422. {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
  423. assert length(modified["object"]["tag"]) == 2
  424. assert is_nil(modified["object"]["emoji"])
  425. assert is_nil(modified["object"]["like_count"])
  426. assert is_nil(modified["object"]["announcements"])
  427. assert is_nil(modified["object"]["announcement_count"])
  428. assert is_nil(modified["object"]["likes"])
  429. end
  430. test "the directMessage flag is present" do
  431. user = insert(:user)
  432. other_user = insert(:user)
  433. {:ok, activity} = CommonAPI.post(user, %{status: "2hu :moominmamma:"})
  434. {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
  435. assert modified["directMessage"] == false
  436. {:ok, activity} = CommonAPI.post(user, %{status: "@#{other_user.nickname} :moominmamma:"})
  437. {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
  438. assert modified["directMessage"] == false
  439. {:ok, activity} =
  440. CommonAPI.post(user, %{
  441. status: "@#{other_user.nickname} :moominmamma:",
  442. visibility: "direct"
  443. })
  444. {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
  445. assert modified["directMessage"] == true
  446. end
  447. test "it strips BCC field" do
  448. user = insert(:user)
  449. {:ok, list} = Pleroma.List.create("foo", user)
  450. {:ok, activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"})
  451. {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
  452. assert is_nil(modified["bcc"])
  453. end
  454. test "it can handle Listen activities" do
  455. listen_activity = insert(:listen)
  456. {:ok, modified} = Transmogrifier.prepare_outgoing(listen_activity.data)
  457. assert modified["type"] == "Listen"
  458. user = insert(:user)
  459. {:ok, activity} = CommonAPI.listen(user, %{"title" => "lain radio episode 1"})
  460. {:ok, _modified} = Transmogrifier.prepare_outgoing(activity.data)
  461. end
  462. test "custom emoji urls are URI encoded" do
  463. # :dinosaur: filename has a space -> dino walking.gif
  464. user = insert(:user)
  465. {:ok, activity} = CommonAPI.post(user, %{status: "everybody do the dinosaur :dinosaur:"})
  466. {:ok, prepared} = Transmogrifier.prepare_outgoing(activity.data)
  467. assert length(prepared["object"]["tag"]) == 1
  468. url = prepared["object"]["tag"] |> List.first() |> Map.get("icon") |> Map.get("url")
  469. assert url == "http://localhost:4001/emoji/dino%20walking.gif"
  470. end
  471. test "Updates of Notes are handled" do
  472. user = insert(:user)
  473. {:ok, activity} = CommonAPI.post(user, %{status: "everybody do the dinosaur :dinosaur:"})
  474. {:ok, update} = CommonAPI.update(activity, user, %{status: "mew mew :blank:"})
  475. {:ok, prepared} = Transmogrifier.prepare_outgoing(update.data)
  476. assert %{
  477. "content" => "mew mew :blank:",
  478. "tag" => [%{"name" => ":blank:", "type" => "Emoji"}],
  479. "formerRepresentations" => %{
  480. "orderedItems" => [
  481. %{
  482. "content" => "everybody do the dinosaur :dinosaur:",
  483. "tag" => [%{"name" => ":dinosaur:", "type" => "Emoji"}]
  484. }
  485. ]
  486. }
  487. } = prepared["object"]
  488. end
  489. test "it prepares a quote post" do
  490. user = insert(:user)
  491. {:ok, quoted_post} = CommonAPI.post(user, %{status: "hey"})
  492. {:ok, quote_post} = CommonAPI.post(user, %{status: "hey", quote_id: quoted_post.id})
  493. {:ok, modified} = Transmogrifier.prepare_outgoing(quote_post.data)
  494. %{data: %{"id" => quote_id}} = Object.normalize(quoted_post)
  495. assert modified["object"]["quoteUrl"] == quote_id
  496. assert modified["object"]["quoteUri"] == quote_id
  497. end
  498. test "it adds language of the object to its json-ld context" do
  499. user = insert(:user)
  500. {:ok, activity} = CommonAPI.post(user, %{status: "Cześć", language: "pl"})
  501. {:ok, modified} = Transmogrifier.prepare_outgoing(activity.object.data)
  502. assert [_, _, %{"@language" => "pl"}] = modified["@context"]
  503. end
  504. test "it adds language of the object to Create activity json-ld context" do
  505. user = insert(:user)
  506. {:ok, activity} = CommonAPI.post(user, %{status: "Cześć", language: "pl"})
  507. {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
  508. assert [_, _, %{"@language" => "pl"}] = modified["@context"]
  509. end
  510. end
  511. describe "actor rewriting" do
  512. test "it fixes the actor URL property to be a proper URI" do
  513. data = %{
  514. "url" => %{"href" => "http://example.com"}
  515. }
  516. rewritten = Transmogrifier.maybe_fix_user_object(data)
  517. assert rewritten["url"] == "http://example.com"
  518. end
  519. end
  520. describe "actor origin containment" do
  521. test "it rejects activities which reference objects with bogus origins" do
  522. data = %{
  523. "@context" => "https://www.w3.org/ns/activitystreams",
  524. "id" => "http://mastodon.example.org/users/admin/activities/1234",
  525. "actor" => "http://mastodon.example.org/users/admin",
  526. "to" => ["https://www.w3.org/ns/activitystreams#Public"],
  527. "object" => "https://info.pleroma.site/activity.json",
  528. "type" => "Announce"
  529. }
  530. assert capture_log(fn ->
  531. {:error, _} = Transmogrifier.handle_incoming(data)
  532. end) =~ "Object rejected while fetching"
  533. end
  534. test "it rejects activities which reference objects that have an incorrect attribution (variant 1)" do
  535. data = %{
  536. "@context" => "https://www.w3.org/ns/activitystreams",
  537. "id" => "http://mastodon.example.org/users/admin/activities/1234",
  538. "actor" => "http://mastodon.example.org/users/admin",
  539. "to" => ["https://www.w3.org/ns/activitystreams#Public"],
  540. "object" => "https://info.pleroma.site/activity2.json",
  541. "type" => "Announce"
  542. }
  543. assert capture_log(fn ->
  544. {:error, _} = Transmogrifier.handle_incoming(data)
  545. end) =~ "Object rejected while fetching"
  546. end
  547. test "it rejects activities which reference objects that have an incorrect attribution (variant 2)" do
  548. data = %{
  549. "@context" => "https://www.w3.org/ns/activitystreams",
  550. "id" => "http://mastodon.example.org/users/admin/activities/1234",
  551. "actor" => "http://mastodon.example.org/users/admin",
  552. "to" => ["https://www.w3.org/ns/activitystreams#Public"],
  553. "object" => "https://info.pleroma.site/activity3.json",
  554. "type" => "Announce"
  555. }
  556. assert capture_log(fn ->
  557. {:error, _} = Transmogrifier.handle_incoming(data)
  558. end) =~ "Object rejected while fetching"
  559. end
  560. end
  561. describe "fix_explicit_addressing" do
  562. setup do
  563. user = insert(:user)
  564. [user: user]
  565. end
  566. test "moves non-explicitly mentioned actors to cc", %{user: user} do
  567. explicitly_mentioned_actors = [
  568. "https://pleroma.gold/users/user1",
  569. "https://pleroma.gold/user2"
  570. ]
  571. object = %{
  572. "actor" => user.ap_id,
  573. "to" => explicitly_mentioned_actors ++ ["https://social.beepboop.ga/users/dirb"],
  574. "cc" => [],
  575. "tag" =>
  576. Enum.map(explicitly_mentioned_actors, fn href ->
  577. %{"type" => "Mention", "href" => href}
  578. end)
  579. }
  580. fixed_object = Transmogrifier.fix_explicit_addressing(object, user.follower_address)
  581. assert Enum.all?(explicitly_mentioned_actors, &(&1 in fixed_object["to"]))
  582. refute "https://social.beepboop.ga/users/dirb" in fixed_object["to"]
  583. assert "https://social.beepboop.ga/users/dirb" in fixed_object["cc"]
  584. end
  585. test "does not move actor's follower collection to cc", %{user: user} do
  586. object = %{
  587. "actor" => user.ap_id,
  588. "to" => [user.follower_address],
  589. "cc" => []
  590. }
  591. fixed_object = Transmogrifier.fix_explicit_addressing(object, user.follower_address)
  592. assert user.follower_address in fixed_object["to"]
  593. refute user.follower_address in fixed_object["cc"]
  594. end
  595. test "removes recipient's follower collection from cc", %{user: user} do
  596. recipient = insert(:user)
  597. object = %{
  598. "actor" => user.ap_id,
  599. "to" => [recipient.ap_id, "https://www.w3.org/ns/activitystreams#Public"],
  600. "cc" => [user.follower_address, recipient.follower_address]
  601. }
  602. fixed_object = Transmogrifier.fix_explicit_addressing(object, user.follower_address)
  603. assert user.follower_address in fixed_object["cc"]
  604. refute recipient.follower_address in fixed_object["cc"]
  605. refute recipient.follower_address in fixed_object["to"]
  606. end
  607. end
  608. describe "fix_summary/1" do
  609. test "returns fixed object" do
  610. assert Transmogrifier.fix_summary(%{"summary" => nil}) == %{"summary" => ""}
  611. assert Transmogrifier.fix_summary(%{"summary" => "ok"}) == %{"summary" => "ok"}
  612. assert Transmogrifier.fix_summary(%{}) == %{"summary" => ""}
  613. end
  614. end
  615. describe "fix_url/1" do
  616. test "fixes data for object when url is map" do
  617. object = %{
  618. "url" => %{
  619. "type" => "Link",
  620. "mimeType" => "video/mp4",
  621. "href" => "https://peede8d-46fb-ad81-2d4c2d1630e3-480.mp4"
  622. }
  623. }
  624. assert Transmogrifier.fix_url(object) == %{
  625. "url" => "https://peede8d-46fb-ad81-2d4c2d1630e3-480.mp4"
  626. }
  627. end
  628. test "returns non-modified object" do
  629. assert Transmogrifier.fix_url(%{"type" => "Text"}) == %{"type" => "Text"}
  630. end
  631. end
  632. describe "get_obj_helper/2" do
  633. test "returns nil when cannot normalize object" do
  634. assert capture_log(fn ->
  635. refute Transmogrifier.get_obj_helper("test-obj-id")
  636. end) =~ "Unsupported URI scheme"
  637. end
  638. test "returns {:ok, %Object{}} for success case" do
  639. assert {:ok, %Object{}} =
  640. Transmogrifier.get_obj_helper(
  641. "https://mstdn.io/users/mayuutann/statuses/99568293732299394"
  642. )
  643. end
  644. end
  645. describe "fix_attachments/1" do
  646. test "puts dimensions into attachment url field" do
  647. object = %{
  648. "attachment" => [
  649. %{
  650. "type" => "Document",
  651. "name" => "Hello world",
  652. "url" => "https://media.example.tld/1.jpg",
  653. "width" => 880,
  654. "height" => 960,
  655. "mediaType" => "image/jpeg",
  656. "blurhash" => "eTKL26+HDjcEIBVl;ds+K6t301W.t7nit7y1E,R:v}ai4nXSt7V@of"
  657. }
  658. ]
  659. }
  660. expected = %{
  661. "attachment" => [
  662. %{
  663. "type" => "Document",
  664. "name" => "Hello world",
  665. "url" => [
  666. %{
  667. "type" => "Link",
  668. "mediaType" => "image/jpeg",
  669. "href" => "https://media.example.tld/1.jpg",
  670. "width" => 880,
  671. "height" => 960
  672. }
  673. ],
  674. "mediaType" => "image/jpeg",
  675. "blurhash" => "eTKL26+HDjcEIBVl;ds+K6t301W.t7nit7y1E,R:v}ai4nXSt7V@of"
  676. }
  677. ]
  678. }
  679. assert Transmogrifier.fix_attachments(object) == expected
  680. end
  681. end
  682. describe "prepare_object/1" do
  683. test "it processes history" do
  684. original = %{
  685. "formerRepresentations" => %{
  686. "orderedItems" => [
  687. %{
  688. "generator" => %{},
  689. "emoji" => %{"blobcat" => "http://localhost:4001/emoji/blobcat.png"}
  690. }
  691. ]
  692. }
  693. }
  694. processed = Transmogrifier.prepare_object(original)
  695. history_item = Enum.at(processed["formerRepresentations"]["orderedItems"], 0)
  696. refute Map.has_key?(history_item, "generator")
  697. assert [%{"name" => ":blobcat:"}] = history_item["tag"]
  698. end
  699. test "it works when there is no or bad history" do
  700. original = %{
  701. "formerRepresentations" => %{
  702. "items" => [
  703. %{
  704. "generator" => %{},
  705. "emoji" => %{"blobcat" => "http://localhost:4001/emoji/blobcat.png"}
  706. }
  707. ]
  708. }
  709. }
  710. processed = Transmogrifier.prepare_object(original)
  711. assert processed["formerRepresentations"] == original["formerRepresentations"]
  712. end
  713. test "it uses contentMap to specify post language" do
  714. user = insert(:user)
  715. {:ok, activity} = CommonAPI.post(user, %{status: "Cześć", language: "pl"})
  716. object = Transmogrifier.prepare_object(activity.object.data)
  717. assert %{"contentMap" => %{"pl" => "Cześć"}} = object
  718. end
  719. end
  720. end