Fork of Pleroma with site-specific changes and feature branches https://git.pleroma.social/pleroma/pleroma
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

624 lines
18KB

  1. # Pleroma: A lightweight social networking server
  2. # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
  3. # SPDX-License-Identifier: AGPL-3.0-only
  4. defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
  5. use Pleroma.DataCase
  6. alias Pleroma.Activity
  7. alias Pleroma.Bookmark
  8. alias Pleroma.Conversation.Participation
  9. alias Pleroma.HTML
  10. alias Pleroma.Object
  11. alias Pleroma.Repo
  12. alias Pleroma.User
  13. alias Pleroma.UserRelationship
  14. alias Pleroma.Web.CommonAPI
  15. alias Pleroma.Web.CommonAPI.Utils
  16. alias Pleroma.Web.MastodonAPI.AccountView
  17. alias Pleroma.Web.MastodonAPI.StatusView
  18. import Pleroma.Factory
  19. import Tesla.Mock
  20. import OpenApiSpex.TestAssertions
  21. setup do
  22. mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
  23. :ok
  24. end
  25. test "has an emoji reaction list" do
  26. user = insert(:user)
  27. other_user = insert(:user)
  28. third_user = insert(:user)
  29. {:ok, activity} = CommonAPI.post(user, %{status: "dae cofe??"})
  30. {:ok, _} = CommonAPI.react_with_emoji(activity.id, user, "☕")
  31. {:ok, _} = CommonAPI.react_with_emoji(activity.id, third_user, "🍵")
  32. {:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "☕")
  33. activity = Repo.get(Activity, activity.id)
  34. status = StatusView.render("show.json", activity: activity)
  35. assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec())
  36. assert status[:pleroma][:emoji_reactions] == [
  37. %{name: "☕", count: 2, me: false},
  38. %{name: "🍵", count: 1, me: false}
  39. ]
  40. status = StatusView.render("show.json", activity: activity, for: user)
  41. assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec())
  42. assert status[:pleroma][:emoji_reactions] == [
  43. %{name: "☕", count: 2, me: true},
  44. %{name: "🍵", count: 1, me: false}
  45. ]
  46. end
  47. test "loads and returns the direct conversation id when given the `with_direct_conversation_id` option" do
  48. user = insert(:user)
  49. {:ok, activity} = CommonAPI.post(user, %{status: "Hey @shp!", visibility: "direct"})
  50. [participation] = Participation.for_user(user)
  51. status =
  52. StatusView.render("show.json",
  53. activity: activity,
  54. with_direct_conversation_id: true,
  55. for: user
  56. )
  57. assert status[:pleroma][:direct_conversation_id] == participation.id
  58. status = StatusView.render("show.json", activity: activity, for: user)
  59. assert status[:pleroma][:direct_conversation_id] == nil
  60. assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec())
  61. end
  62. test "returns the direct conversation id when given the `direct_conversation_id` option" do
  63. user = insert(:user)
  64. {:ok, activity} = CommonAPI.post(user, %{status: "Hey @shp!", visibility: "direct"})
  65. [participation] = Participation.for_user(user)
  66. status =
  67. StatusView.render("show.json",
  68. activity: activity,
  69. direct_conversation_id: participation.id,
  70. for: user
  71. )
  72. assert status[:pleroma][:direct_conversation_id] == participation.id
  73. assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec())
  74. end
  75. test "returns a temporary ap_id based user for activities missing db users" do
  76. user = insert(:user)
  77. {:ok, activity} = CommonAPI.post(user, %{status: "Hey @shp!", visibility: "direct"})
  78. Repo.delete(user)
  79. Cachex.clear(:user_cache)
  80. finger_url =
  81. "https://localhost/.well-known/webfinger?resource=acct:#{user.nickname}@localhost"
  82. Tesla.Mock.mock_global(fn
  83. %{method: :get, url: "http://localhost/.well-known/host-meta"} ->
  84. %Tesla.Env{status: 404, body: ""}
  85. %{method: :get, url: "https://localhost/.well-known/host-meta"} ->
  86. %Tesla.Env{status: 404, body: ""}
  87. %{
  88. method: :get,
  89. url: ^finger_url
  90. } ->
  91. %Tesla.Env{status: 404, body: ""}
  92. end)
  93. %{account: ms_user} = StatusView.render("show.json", activity: activity)
  94. assert ms_user.acct == "erroruser@example.com"
  95. end
  96. test "tries to get a user by nickname if fetching by ap_id doesn't work" do
  97. user = insert(:user)
  98. {:ok, activity} = CommonAPI.post(user, %{status: "Hey @shp!", visibility: "direct"})
  99. {:ok, user} =
  100. user
  101. |> Ecto.Changeset.change(%{ap_id: "#{user.ap_id}/extension/#{user.nickname}"})
  102. |> Repo.update()
  103. Cachex.clear(:user_cache)
  104. result = StatusView.render("show.json", activity: activity)
  105. assert result[:account][:id] == to_string(user.id)
  106. assert_schema(result, "Status", Pleroma.Web.ApiSpec.spec())
  107. end
  108. test "a note with null content" do
  109. note = insert(:note_activity)
  110. note_object = Object.normalize(note)
  111. data =
  112. note_object.data
  113. |> Map.put("content", nil)
  114. Object.change(note_object, %{data: data})
  115. |> Object.update_and_set_cache()
  116. User.get_cached_by_ap_id(note.data["actor"])
  117. status = StatusView.render("show.json", %{activity: note})
  118. assert status.content == ""
  119. assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec())
  120. end
  121. test "a note activity" do
  122. note = insert(:note_activity)
  123. object_data = Object.normalize(note).data
  124. user = User.get_cached_by_ap_id(note.data["actor"])
  125. convo_id = Utils.context_to_conversation_id(object_data["context"])
  126. status = StatusView.render("show.json", %{activity: note})
  127. created_at =
  128. (object_data["published"] || "")
  129. |> String.replace(~r/\.\d+Z/, ".000Z")
  130. expected = %{
  131. id: to_string(note.id),
  132. uri: object_data["id"],
  133. url: Pleroma.Web.Router.Helpers.o_status_url(Pleroma.Web.Endpoint, :notice, note),
  134. account: AccountView.render("show.json", %{user: user}),
  135. in_reply_to_id: nil,
  136. in_reply_to_account_id: nil,
  137. card: nil,
  138. reblog: nil,
  139. content: HTML.filter_tags(object_data["content"]),
  140. created_at: created_at,
  141. reblogs_count: 0,
  142. replies_count: 0,
  143. favourites_count: 0,
  144. reblogged: false,
  145. bookmarked: false,
  146. favourited: false,
  147. muted: false,
  148. pinned: false,
  149. sensitive: false,
  150. poll: nil,
  151. spoiler_text: HTML.filter_tags(object_data["summary"]),
  152. visibility: "public",
  153. media_attachments: [],
  154. mentions: [],
  155. tags: [
  156. %{
  157. name: "#{object_data["tag"]}",
  158. url: "/tag/#{object_data["tag"]}"
  159. }
  160. ],
  161. application: %{
  162. name: "Web",
  163. website: nil
  164. },
  165. language: nil,
  166. emojis: [
  167. %{
  168. shortcode: "2hu",
  169. url: "corndog.png",
  170. static_url: "corndog.png",
  171. visible_in_picker: false
  172. }
  173. ],
  174. pleroma: %{
  175. local: true,
  176. conversation_id: convo_id,
  177. in_reply_to_account_acct: nil,
  178. content: %{"text/plain" => HTML.strip_tags(object_data["content"])},
  179. spoiler_text: %{"text/plain" => HTML.strip_tags(object_data["summary"])},
  180. expires_at: nil,
  181. direct_conversation_id: nil,
  182. thread_muted: false,
  183. emoji_reactions: []
  184. }
  185. }
  186. assert status == expected
  187. assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec())
  188. end
  189. test "tells if the message is muted for some reason" do
  190. user = insert(:user)
  191. other_user = insert(:user)
  192. {:ok, _user_relationships} = User.mute(user, other_user)
  193. {:ok, activity} = CommonAPI.post(other_user, %{status: "test"})
  194. relationships_opt = UserRelationship.view_relationships_option(user, [other_user])
  195. opts = %{activity: activity}
  196. status = StatusView.render("show.json", opts)
  197. assert status.muted == false
  198. assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec())
  199. status = StatusView.render("show.json", Map.put(opts, :relationships, relationships_opt))
  200. assert status.muted == false
  201. for_opts = %{activity: activity, for: user}
  202. status = StatusView.render("show.json", for_opts)
  203. assert status.muted == true
  204. status = StatusView.render("show.json", Map.put(for_opts, :relationships, relationships_opt))
  205. assert status.muted == true
  206. assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec())
  207. end
  208. test "tells if the message is thread muted" do
  209. user = insert(:user)
  210. other_user = insert(:user)
  211. {:ok, _user_relationships} = User.mute(user, other_user)
  212. {:ok, activity} = CommonAPI.post(other_user, %{status: "test"})
  213. status = StatusView.render("show.json", %{activity: activity, for: user})
  214. assert status.pleroma.thread_muted == false
  215. {:ok, activity} = CommonAPI.add_mute(user, activity)
  216. status = StatusView.render("show.json", %{activity: activity, for: user})
  217. assert status.pleroma.thread_muted == true
  218. end
  219. test "tells if the status is bookmarked" do
  220. user = insert(:user)
  221. {:ok, activity} = CommonAPI.post(user, %{status: "Cute girls doing cute things"})
  222. status = StatusView.render("show.json", %{activity: activity})
  223. assert status.bookmarked == false
  224. status = StatusView.render("show.json", %{activity: activity, for: user})
  225. assert status.bookmarked == false
  226. {:ok, _bookmark} = Bookmark.create(user.id, activity.id)
  227. activity = Activity.get_by_id_with_object(activity.id)
  228. status = StatusView.render("show.json", %{activity: activity, for: user})
  229. assert status.bookmarked == true
  230. end
  231. test "a reply" do
  232. note = insert(:note_activity)
  233. user = insert(:user)
  234. {:ok, activity} = CommonAPI.post(user, %{status: "he", in_reply_to_status_id: note.id})
  235. status = StatusView.render("show.json", %{activity: activity})
  236. assert status.in_reply_to_id == to_string(note.id)
  237. [status] = StatusView.render("index.json", %{activities: [activity], as: :activity})
  238. assert status.in_reply_to_id == to_string(note.id)
  239. end
  240. test "contains mentions" do
  241. user = insert(:user)
  242. mentioned = insert(:user)
  243. {:ok, activity} = CommonAPI.post(user, %{status: "hi @#{mentioned.nickname}"})
  244. status = StatusView.render("show.json", %{activity: activity})
  245. assert status.mentions ==
  246. Enum.map([mentioned], fn u -> AccountView.render("mention.json", %{user: u}) end)
  247. assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec())
  248. end
  249. test "create mentions from the 'to' field" do
  250. %User{ap_id: recipient_ap_id} = insert(:user)
  251. cc = insert_pair(:user) |> Enum.map(& &1.ap_id)
  252. object =
  253. insert(:note, %{
  254. data: %{
  255. "to" => [recipient_ap_id],
  256. "cc" => cc
  257. }
  258. })
  259. activity =
  260. insert(:note_activity, %{
  261. note: object,
  262. recipients: [recipient_ap_id | cc]
  263. })
  264. assert length(activity.recipients) == 3
  265. %{mentions: [mention] = mentions} = StatusView.render("show.json", %{activity: activity})
  266. assert length(mentions) == 1
  267. assert mention.url == recipient_ap_id
  268. end
  269. test "create mentions from the 'tag' field" do
  270. recipient = insert(:user)
  271. cc = insert_pair(:user) |> Enum.map(& &1.ap_id)
  272. object =
  273. insert(:note, %{
  274. data: %{
  275. "cc" => cc,
  276. "tag" => [
  277. %{
  278. "href" => recipient.ap_id,
  279. "name" => recipient.nickname,
  280. "type" => "Mention"
  281. },
  282. %{
  283. "href" => "https://example.com/search?tag=test",
  284. "name" => "#test",
  285. "type" => "Hashtag"
  286. }
  287. ]
  288. }
  289. })
  290. activity =
  291. insert(:note_activity, %{
  292. note: object,
  293. recipients: [recipient.ap_id | cc]
  294. })
  295. assert length(activity.recipients) == 3
  296. %{mentions: [mention] = mentions} = StatusView.render("show.json", %{activity: activity})
  297. assert length(mentions) == 1
  298. assert mention.url == recipient.ap_id
  299. end
  300. test "attachments" do
  301. object = %{
  302. "type" => "Image",
  303. "url" => [
  304. %{
  305. "mediaType" => "image/png",
  306. "href" => "someurl"
  307. }
  308. ],
  309. "uuid" => 6
  310. }
  311. expected = %{
  312. id: "1638338801",
  313. type: "image",
  314. url: "someurl",
  315. remote_url: "someurl",
  316. preview_url: "someurl",
  317. text_url: "someurl",
  318. description: nil,
  319. pleroma: %{mime_type: "image/png"}
  320. }
  321. api_spec = Pleroma.Web.ApiSpec.spec()
  322. assert expected == StatusView.render("attachment.json", %{attachment: object})
  323. assert_schema(expected, "Attachment", api_spec)
  324. # If theres a "id", use that instead of the generated one
  325. object = Map.put(object, "id", 2)
  326. result = StatusView.render("attachment.json", %{attachment: object})
  327. assert %{id: "2"} = result
  328. assert_schema(result, "Attachment", api_spec)
  329. end
  330. test "put the url advertised in the Activity in to the url attribute" do
  331. id = "https://wedistribute.org/wp-json/pterotype/v1/object/85810"
  332. [activity] = Activity.search(nil, id)
  333. status = StatusView.render("show.json", %{activity: activity})
  334. assert status.uri == id
  335. assert status.url == "https://wedistribute.org/2019/07/mastodon-drops-ostatus/"
  336. end
  337. test "a reblog" do
  338. user = insert(:user)
  339. activity = insert(:note_activity)
  340. {:ok, reblog} = CommonAPI.repeat(activity.id, user)
  341. represented = StatusView.render("show.json", %{for: user, activity: reblog})
  342. assert represented[:id] == to_string(reblog.id)
  343. assert represented[:reblog][:id] == to_string(activity.id)
  344. assert represented[:emojis] == []
  345. assert_schema(represented, "Status", Pleroma.Web.ApiSpec.spec())
  346. end
  347. test "a peertube video" do
  348. user = insert(:user)
  349. {:ok, object} =
  350. Pleroma.Object.Fetcher.fetch_object_from_id(
  351. "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3"
  352. )
  353. %Activity{} = activity = Activity.get_create_by_object_ap_id(object.data["id"])
  354. represented = StatusView.render("show.json", %{for: user, activity: activity})
  355. assert represented[:id] == to_string(activity.id)
  356. assert length(represented[:media_attachments]) == 1
  357. assert_schema(represented, "Status", Pleroma.Web.ApiSpec.spec())
  358. end
  359. test "funkwhale audio" do
  360. user = insert(:user)
  361. {:ok, object} =
  362. Pleroma.Object.Fetcher.fetch_object_from_id(
  363. "https://channels.tests.funkwhale.audio/federation/music/uploads/42342395-0208-4fee-a38d-259a6dae0871"
  364. )
  365. %Activity{} = activity = Activity.get_create_by_object_ap_id(object.data["id"])
  366. represented = StatusView.render("show.json", %{for: user, activity: activity})
  367. assert represented[:id] == to_string(activity.id)
  368. assert length(represented[:media_attachments]) == 1
  369. end
  370. test "a Mobilizon event" do
  371. user = insert(:user)
  372. {:ok, object} =
  373. Pleroma.Object.Fetcher.fetch_object_from_id(
  374. "https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39"
  375. )
  376. %Activity{} = activity = Activity.get_create_by_object_ap_id(object.data["id"])
  377. represented = StatusView.render("show.json", %{for: user, activity: activity})
  378. assert represented[:id] == to_string(activity.id)
  379. end
  380. describe "build_tags/1" do
  381. test "it returns a a dictionary tags" do
  382. object_tags = [
  383. "fediverse",
  384. "mastodon",
  385. "nextcloud",
  386. %{
  387. "href" => "https://kawen.space/users/lain",
  388. "name" => "@lain@kawen.space",
  389. "type" => "Mention"
  390. }
  391. ]
  392. assert StatusView.build_tags(object_tags) == [
  393. %{name: "fediverse", url: "/tag/fediverse"},
  394. %{name: "mastodon", url: "/tag/mastodon"},
  395. %{name: "nextcloud", url: "/tag/nextcloud"}
  396. ]
  397. end
  398. end
  399. describe "rich media cards" do
  400. test "a rich media card without a site name renders correctly" do
  401. page_url = "http://example.com"
  402. card = %{
  403. url: page_url,
  404. image: page_url <> "/example.jpg",
  405. title: "Example website"
  406. }
  407. %{provider_name: "example.com"} =
  408. StatusView.render("card.json", %{page_url: page_url, rich_media: card})
  409. end
  410. test "a rich media card without a site name or image renders correctly" do
  411. page_url = "http://example.com"
  412. card = %{
  413. url: page_url,
  414. title: "Example website"
  415. }
  416. %{provider_name: "example.com"} =
  417. StatusView.render("card.json", %{page_url: page_url, rich_media: card})
  418. end
  419. test "a rich media card without an image renders correctly" do
  420. page_url = "http://example.com"
  421. card = %{
  422. url: page_url,
  423. site_name: "Example site name",
  424. title: "Example website"
  425. }
  426. %{provider_name: "example.com"} =
  427. StatusView.render("card.json", %{page_url: page_url, rich_media: card})
  428. end
  429. test "a rich media card with all relevant data renders correctly" do
  430. page_url = "http://example.com"
  431. card = %{
  432. url: page_url,
  433. site_name: "Example site name",
  434. title: "Example website",
  435. image: page_url <> "/example.jpg",
  436. description: "Example description"
  437. }
  438. %{provider_name: "example.com"} =
  439. StatusView.render("card.json", %{page_url: page_url, rich_media: card})
  440. end
  441. end
  442. test "does not embed a relationship in the account" do
  443. user = insert(:user)
  444. other_user = insert(:user)
  445. {:ok, activity} =
  446. CommonAPI.post(user, %{
  447. status: "drink more water"
  448. })
  449. result = StatusView.render("show.json", %{activity: activity, for: other_user})
  450. assert result[:account][:pleroma][:relationship] == %{}
  451. assert_schema(result, "Status", Pleroma.Web.ApiSpec.spec())
  452. end
  453. test "does not embed a relationship in the account in reposts" do
  454. user = insert(:user)
  455. other_user = insert(:user)
  456. {:ok, activity} =
  457. CommonAPI.post(user, %{
  458. status: "˙˙ɐʎns"
  459. })
  460. {:ok, activity} = CommonAPI.repeat(activity.id, other_user)
  461. result = StatusView.render("show.json", %{activity: activity, for: user})
  462. assert result[:account][:pleroma][:relationship] == %{}
  463. assert result[:reblog][:account][:pleroma][:relationship] == %{}
  464. assert_schema(result, "Status", Pleroma.Web.ApiSpec.spec())
  465. end
  466. test "visibility/list" do
  467. user = insert(:user)
  468. {:ok, list} = Pleroma.List.create("foo", user)
  469. {:ok, activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"})
  470. status = StatusView.render("show.json", activity: activity)
  471. assert status.visibility == "list"
  472. end
  473. end