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.

326 lines
11KB

  1. # Pleroma: A lightweight social networking server
  2. # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
  3. # SPDX-License-Identifier: AGPL-3.0-only
  4. defmodule Pleroma.ObjectTest do
  5. use Pleroma.DataCase
  6. use Oban.Testing, repo: Pleroma.Repo
  7. import ExUnit.CaptureLog
  8. import Pleroma.Factory
  9. import Tesla.Mock
  10. alias Pleroma.Activity
  11. alias Pleroma.Object
  12. alias Pleroma.Repo
  13. alias Pleroma.Tests.ObanHelpers
  14. alias Pleroma.Web.CommonAPI
  15. setup do
  16. mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
  17. :ok
  18. end
  19. test "returns an object by it's AP id" do
  20. object = insert(:note)
  21. found_object = Object.get_by_ap_id(object.data["id"])
  22. assert object == found_object
  23. end
  24. describe "generic changeset" do
  25. test "it ensures uniqueness of the id" do
  26. object = insert(:note)
  27. cs = Object.change(%Object{}, %{data: %{id: object.data["id"]}})
  28. assert cs.valid?
  29. {:error, _result} = Repo.insert(cs)
  30. end
  31. end
  32. describe "deletion function" do
  33. test "deletes an object" do
  34. object = insert(:note)
  35. found_object = Object.get_by_ap_id(object.data["id"])
  36. assert object == found_object
  37. Object.delete(found_object)
  38. found_object = Object.get_by_ap_id(object.data["id"])
  39. refute object == found_object
  40. assert found_object.data["type"] == "Tombstone"
  41. end
  42. test "ensures cache is cleared for the object" do
  43. object = insert(:note)
  44. cached_object = Object.get_cached_by_ap_id(object.data["id"])
  45. assert object == cached_object
  46. Cachex.put(:web_resp_cache, URI.parse(object.data["id"]).path, "cofe")
  47. Object.delete(cached_object)
  48. {:ok, nil} = Cachex.get(:object_cache, "object:#{object.data["id"]}")
  49. {:ok, nil} = Cachex.get(:web_resp_cache, URI.parse(object.data["id"]).path)
  50. cached_object = Object.get_cached_by_ap_id(object.data["id"])
  51. refute object == cached_object
  52. assert cached_object.data["type"] == "Tombstone"
  53. end
  54. end
  55. describe "delete attachments" do
  56. clear_config([Pleroma.Upload])
  57. test "in subdirectories" do
  58. Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local)
  59. file = %Plug.Upload{
  60. content_type: "image/jpg",
  61. path: Path.absname("test/fixtures/image.jpg"),
  62. filename: "an_image.jpg"
  63. }
  64. user = insert(:user)
  65. {:ok, %Object{} = attachment} =
  66. Pleroma.Web.ActivityPub.ActivityPub.upload(file, actor: user.ap_id)
  67. %{data: %{"attachment" => [%{"url" => [%{"href" => href}]}]}} =
  68. note = insert(:note, %{user: user, data: %{"attachment" => [attachment.data]}})
  69. uploads_dir = Pleroma.Config.get!([Pleroma.Uploaders.Local, :uploads])
  70. path = href |> Path.dirname() |> Path.basename()
  71. assert {:ok, ["an_image.jpg"]} == File.ls("#{uploads_dir}/#{path}")
  72. Object.delete(note)
  73. ObanHelpers.perform(all_enqueued(worker: Pleroma.Workers.AttachmentsCleanupWorker))
  74. assert Object.get_by_id(attachment.id) == nil
  75. assert {:ok, []} == File.ls("#{uploads_dir}/#{path}")
  76. end
  77. test "with dedupe enabled" do
  78. Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local)
  79. Pleroma.Config.put([Pleroma.Upload, :filters], [Pleroma.Upload.Filter.Dedupe])
  80. uploads_dir = Pleroma.Config.get!([Pleroma.Uploaders.Local, :uploads])
  81. File.mkdir_p!(uploads_dir)
  82. file = %Plug.Upload{
  83. content_type: "image/jpg",
  84. path: Path.absname("test/fixtures/image.jpg"),
  85. filename: "an_image.jpg"
  86. }
  87. user = insert(:user)
  88. {:ok, %Object{} = attachment} =
  89. Pleroma.Web.ActivityPub.ActivityPub.upload(file, actor: user.ap_id)
  90. %{data: %{"attachment" => [%{"url" => [%{"href" => href}]}]}} =
  91. note = insert(:note, %{user: user, data: %{"attachment" => [attachment.data]}})
  92. filename = Path.basename(href)
  93. assert {:ok, files} = File.ls(uploads_dir)
  94. assert filename in files
  95. Object.delete(note)
  96. ObanHelpers.perform(all_enqueued(worker: Pleroma.Workers.AttachmentsCleanupWorker))
  97. assert Object.get_by_id(attachment.id) == nil
  98. assert {:ok, files} = File.ls(uploads_dir)
  99. refute filename in files
  100. end
  101. test "with objects that have legacy data.url attribute" do
  102. Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local)
  103. file = %Plug.Upload{
  104. content_type: "image/jpg",
  105. path: Path.absname("test/fixtures/image.jpg"),
  106. filename: "an_image.jpg"
  107. }
  108. user = insert(:user)
  109. {:ok, %Object{} = attachment} =
  110. Pleroma.Web.ActivityPub.ActivityPub.upload(file, actor: user.ap_id)
  111. {:ok, %Object{}} = Object.create(%{url: "https://google.com", actor: user.ap_id})
  112. %{data: %{"attachment" => [%{"url" => [%{"href" => href}]}]}} =
  113. note = insert(:note, %{user: user, data: %{"attachment" => [attachment.data]}})
  114. uploads_dir = Pleroma.Config.get!([Pleroma.Uploaders.Local, :uploads])
  115. path = href |> Path.dirname() |> Path.basename()
  116. assert {:ok, ["an_image.jpg"]} == File.ls("#{uploads_dir}/#{path}")
  117. Object.delete(note)
  118. ObanHelpers.perform(all_enqueued(worker: Pleroma.Workers.AttachmentsCleanupWorker))
  119. assert Object.get_by_id(attachment.id) == nil
  120. assert {:ok, []} == File.ls("#{uploads_dir}/#{path}")
  121. end
  122. end
  123. describe "normalizer" do
  124. test "fetches unknown objects by default" do
  125. %Object{} =
  126. object = Object.normalize("http://mastodon.example.org/@admin/99541947525187367")
  127. assert object.data["url"] == "http://mastodon.example.org/@admin/99541947525187367"
  128. end
  129. test "fetches unknown objects when fetch_remote is explicitly true" do
  130. %Object{} =
  131. object = Object.normalize("http://mastodon.example.org/@admin/99541947525187367", true)
  132. assert object.data["url"] == "http://mastodon.example.org/@admin/99541947525187367"
  133. end
  134. test "does not fetch unknown objects when fetch_remote is false" do
  135. assert is_nil(
  136. Object.normalize("http://mastodon.example.org/@admin/99541947525187367", false)
  137. )
  138. end
  139. end
  140. describe "get_by_id_and_maybe_refetch" do
  141. setup do
  142. mock(fn
  143. %{method: :get, url: "https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d"} ->
  144. %Tesla.Env{status: 200, body: File.read!("test/fixtures/tesla_mock/poll_original.json")}
  145. env ->
  146. apply(HttpRequestMock, :request, [env])
  147. end)
  148. mock_modified = fn resp ->
  149. mock(fn
  150. %{method: :get, url: "https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d"} ->
  151. resp
  152. env ->
  153. apply(HttpRequestMock, :request, [env])
  154. end)
  155. end
  156. on_exit(fn -> mock(fn env -> apply(HttpRequestMock, :request, [env]) end) end)
  157. [mock_modified: mock_modified]
  158. end
  159. test "refetches if the time since the last refetch is greater than the interval", %{
  160. mock_modified: mock_modified
  161. } do
  162. %Object{} =
  163. object = Object.normalize("https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d")
  164. Object.set_cache(object)
  165. assert Enum.at(object.data["oneOf"], 0)["replies"]["totalItems"] == 4
  166. assert Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 0
  167. mock_modified.(%Tesla.Env{
  168. status: 200,
  169. body: File.read!("test/fixtures/tesla_mock/poll_modified.json")
  170. })
  171. updated_object = Object.get_by_id_and_maybe_refetch(object.id, interval: -1)
  172. object_in_cache = Object.get_cached_by_ap_id(object.data["id"])
  173. assert updated_object == object_in_cache
  174. assert Enum.at(updated_object.data["oneOf"], 0)["replies"]["totalItems"] == 8
  175. assert Enum.at(updated_object.data["oneOf"], 1)["replies"]["totalItems"] == 3
  176. end
  177. test "returns the old object if refetch fails", %{mock_modified: mock_modified} do
  178. %Object{} =
  179. object = Object.normalize("https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d")
  180. Object.set_cache(object)
  181. assert Enum.at(object.data["oneOf"], 0)["replies"]["totalItems"] == 4
  182. assert Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 0
  183. assert capture_log(fn ->
  184. mock_modified.(%Tesla.Env{status: 404, body: ""})
  185. updated_object = Object.get_by_id_and_maybe_refetch(object.id, interval: -1)
  186. object_in_cache = Object.get_cached_by_ap_id(object.data["id"])
  187. assert updated_object == object_in_cache
  188. assert Enum.at(updated_object.data["oneOf"], 0)["replies"]["totalItems"] == 4
  189. assert Enum.at(updated_object.data["oneOf"], 1)["replies"]["totalItems"] == 0
  190. end) =~
  191. "[error] Couldn't refresh https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d"
  192. end
  193. test "does not refetch if the time since the last refetch is greater than the interval", %{
  194. mock_modified: mock_modified
  195. } do
  196. %Object{} =
  197. object = Object.normalize("https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d")
  198. Object.set_cache(object)
  199. assert Enum.at(object.data["oneOf"], 0)["replies"]["totalItems"] == 4
  200. assert Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 0
  201. mock_modified.(%Tesla.Env{
  202. status: 200,
  203. body: File.read!("test/fixtures/tesla_mock/poll_modified.json")
  204. })
  205. updated_object = Object.get_by_id_and_maybe_refetch(object.id, interval: 100)
  206. object_in_cache = Object.get_cached_by_ap_id(object.data["id"])
  207. assert updated_object == object_in_cache
  208. assert Enum.at(updated_object.data["oneOf"], 0)["replies"]["totalItems"] == 4
  209. assert Enum.at(updated_object.data["oneOf"], 1)["replies"]["totalItems"] == 0
  210. end
  211. test "preserves internal fields on refetch", %{mock_modified: mock_modified} do
  212. %Object{} =
  213. object = Object.normalize("https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d")
  214. Object.set_cache(object)
  215. assert Enum.at(object.data["oneOf"], 0)["replies"]["totalItems"] == 4
  216. assert Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 0
  217. user = insert(:user)
  218. activity = Activity.get_create_by_object_ap_id(object.data["id"])
  219. {:ok, _activity, object} = CommonAPI.favorite(activity.id, user)
  220. assert object.data["like_count"] == 1
  221. mock_modified.(%Tesla.Env{
  222. status: 200,
  223. body: File.read!("test/fixtures/tesla_mock/poll_modified.json")
  224. })
  225. updated_object = Object.get_by_id_and_maybe_refetch(object.id, interval: -1)
  226. object_in_cache = Object.get_cached_by_ap_id(object.data["id"])
  227. assert updated_object == object_in_cache
  228. assert Enum.at(updated_object.data["oneOf"], 0)["replies"]["totalItems"] == 8
  229. assert Enum.at(updated_object.data["oneOf"], 1)["replies"]["totalItems"] == 3
  230. assert updated_object.data["like_count"] == 1
  231. end
  232. end
  233. end