1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.ObjectTest do
7 use Oban.Testing, repo: Pleroma.Repo
8 import ExUnit.CaptureLog
11 alias Pleroma.Activity
14 alias Pleroma.Tests.ObanHelpers
15 alias Pleroma.Web.CommonAPI
18 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
22 test "returns an object by it's AP id" do
23 object = insert(:note)
24 found_object = Object.get_by_ap_id(object.data["id"])
26 assert object == found_object
29 describe "generic changeset" do
30 test "it ensures uniqueness of the id" do
31 object = insert(:note)
32 cs = Object.change(%Object{}, %{data: %{id: object.data["id"]}})
35 {:error, _result} = Repo.insert(cs)
39 describe "deletion function" do
40 test "deletes an object" do
41 object = insert(:note)
42 found_object = Object.get_by_ap_id(object.data["id"])
44 assert object == found_object
46 Object.delete(found_object)
48 found_object = Object.get_by_ap_id(object.data["id"])
50 refute object == found_object
52 assert found_object.data["type"] == "Tombstone"
55 test "ensures cache is cleared for the object" do
56 object = insert(:note)
57 cached_object = Object.get_cached_by_ap_id(object.data["id"])
59 assert object == cached_object
61 Cachex.put(:web_resp_cache, URI.parse(object.data["id"]).path, "cofe")
63 Object.delete(cached_object)
65 {:ok, nil} = Cachex.get(:object_cache, "object:#{object.data["id"]}")
66 {:ok, nil} = Cachex.get(:web_resp_cache, URI.parse(object.data["id"]).path)
68 cached_object = Object.get_cached_by_ap_id(object.data["id"])
70 refute object == cached_object
72 assert cached_object.data["type"] == "Tombstone"
76 describe "delete attachments" do
77 clear_config([Pleroma.Upload])
79 test "in subdirectories" do
80 Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local)
83 content_type: "image/jpg",
84 path: Path.absname("test/fixtures/image.jpg"),
85 filename: "an_image.jpg"
90 {:ok, %Object{} = attachment} =
91 Pleroma.Web.ActivityPub.ActivityPub.upload(file, actor: user.ap_id)
93 %{data: %{"attachment" => [%{"url" => [%{"href" => href}]}]}} =
94 note = insert(:note, %{user: user, data: %{"attachment" => [attachment.data]}})
96 uploads_dir = Pleroma.Config.get!([Pleroma.Uploaders.Local, :uploads])
98 path = href |> Path.dirname() |> Path.basename()
100 assert {:ok, ["an_image.jpg"]} == File.ls("#{uploads_dir}/#{path}")
104 ObanHelpers.perform(all_enqueued(worker: Pleroma.Workers.AttachmentsCleanupWorker))
106 assert Object.get_by_id(attachment.id) == nil
108 assert {:ok, []} == File.ls("#{uploads_dir}/#{path}")
111 test "with dedupe enabled" do
112 Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local)
113 Pleroma.Config.put([Pleroma.Upload, :filters], [Pleroma.Upload.Filter.Dedupe])
115 uploads_dir = Pleroma.Config.get!([Pleroma.Uploaders.Local, :uploads])
117 File.mkdir_p!(uploads_dir)
120 content_type: "image/jpg",
121 path: Path.absname("test/fixtures/image.jpg"),
122 filename: "an_image.jpg"
127 {:ok, %Object{} = attachment} =
128 Pleroma.Web.ActivityPub.ActivityPub.upload(file, actor: user.ap_id)
130 %{data: %{"attachment" => [%{"url" => [%{"href" => href}]}]}} =
131 note = insert(:note, %{user: user, data: %{"attachment" => [attachment.data]}})
133 filename = Path.basename(href)
135 assert {:ok, files} = File.ls(uploads_dir)
136 assert filename in files
140 ObanHelpers.perform(all_enqueued(worker: Pleroma.Workers.AttachmentsCleanupWorker))
142 assert Object.get_by_id(attachment.id) == nil
143 assert {:ok, files} = File.ls(uploads_dir)
144 refute filename in files
148 describe "normalizer" do
149 test "fetches unknown objects by default" do
151 object = Object.normalize("http://mastodon.example.org/@admin/99541947525187367")
153 assert object.data["url"] == "http://mastodon.example.org/@admin/99541947525187367"
156 test "fetches unknown objects when fetch_remote is explicitly true" do
158 object = Object.normalize("http://mastodon.example.org/@admin/99541947525187367", true)
160 assert object.data["url"] == "http://mastodon.example.org/@admin/99541947525187367"
163 test "does not fetch unknown objects when fetch_remote is false" do
165 Object.normalize("http://mastodon.example.org/@admin/99541947525187367", false)
170 describe "get_by_id_and_maybe_refetch" do
173 %{method: :get, url: "https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d"} ->
174 %Tesla.Env{status: 200, body: File.read!("test/fixtures/tesla_mock/poll_original.json")}
177 apply(HttpRequestMock, :request, [env])
180 mock_modified = fn resp ->
182 %{method: :get, url: "https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d"} ->
186 apply(HttpRequestMock, :request, [env])
190 on_exit(fn -> mock(fn env -> apply(HttpRequestMock, :request, [env]) end) end)
192 [mock_modified: mock_modified]
195 test "refetches if the time since the last refetch is greater than the interval", %{
196 mock_modified: mock_modified
199 object = Object.normalize("https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d")
201 Object.set_cache(object)
203 assert Enum.at(object.data["oneOf"], 0)["replies"]["totalItems"] == 4
204 assert Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 0
206 mock_modified.(%Tesla.Env{
208 body: File.read!("test/fixtures/tesla_mock/poll_modified.json")
211 updated_object = Object.get_by_id_and_maybe_refetch(object.id, interval: -1)
212 object_in_cache = Object.get_cached_by_ap_id(object.data["id"])
213 assert updated_object == object_in_cache
214 assert Enum.at(updated_object.data["oneOf"], 0)["replies"]["totalItems"] == 8
215 assert Enum.at(updated_object.data["oneOf"], 1)["replies"]["totalItems"] == 3
218 test "returns the old object if refetch fails", %{mock_modified: mock_modified} do
220 object = Object.normalize("https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d")
222 Object.set_cache(object)
224 assert Enum.at(object.data["oneOf"], 0)["replies"]["totalItems"] == 4
225 assert Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 0
227 assert capture_log(fn ->
228 mock_modified.(%Tesla.Env{status: 404, body: ""})
230 updated_object = Object.get_by_id_and_maybe_refetch(object.id, interval: -1)
231 object_in_cache = Object.get_cached_by_ap_id(object.data["id"])
232 assert updated_object == object_in_cache
233 assert Enum.at(updated_object.data["oneOf"], 0)["replies"]["totalItems"] == 4
234 assert Enum.at(updated_object.data["oneOf"], 1)["replies"]["totalItems"] == 0
236 "[error] Couldn't refresh https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d"
239 test "does not refetch if the time since the last refetch is greater than the interval", %{
240 mock_modified: mock_modified
243 object = Object.normalize("https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d")
245 Object.set_cache(object)
247 assert Enum.at(object.data["oneOf"], 0)["replies"]["totalItems"] == 4
248 assert Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 0
250 mock_modified.(%Tesla.Env{
252 body: File.read!("test/fixtures/tesla_mock/poll_modified.json")
255 updated_object = Object.get_by_id_and_maybe_refetch(object.id, interval: 100)
256 object_in_cache = Object.get_cached_by_ap_id(object.data["id"])
257 assert updated_object == object_in_cache
258 assert Enum.at(updated_object.data["oneOf"], 0)["replies"]["totalItems"] == 4
259 assert Enum.at(updated_object.data["oneOf"], 1)["replies"]["totalItems"] == 0
262 test "preserves internal fields on refetch", %{mock_modified: mock_modified} do
264 object = Object.normalize("https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d")
266 Object.set_cache(object)
268 assert Enum.at(object.data["oneOf"], 0)["replies"]["totalItems"] == 4
269 assert Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 0
272 activity = Activity.get_create_by_object_ap_id(object.data["id"])
273 {:ok, _activity, object} = CommonAPI.favorite(activity.id, user)
275 assert object.data["like_count"] == 1
277 mock_modified.(%Tesla.Env{
279 body: File.read!("test/fixtures/tesla_mock/poll_modified.json")
282 updated_object = Object.get_by_id_and_maybe_refetch(object.id, interval: -1)
283 object_in_cache = Object.get_cached_by_ap_id(object.data["id"])
284 assert updated_object == object_in_cache
285 assert Enum.at(updated_object.data["oneOf"], 0)["replies"]["totalItems"] == 8
286 assert Enum.at(updated_object.data["oneOf"], 1)["replies"]["totalItems"] == 3
288 assert updated_object.data["like_count"] == 1