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 import ExUnit.CaptureLog
10 alias Pleroma.Activity
13 alias Pleroma.Web.CommonAPI
16 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
20 test "returns an object by it's AP id" do
21 object = insert(:note)
22 found_object = Object.get_by_ap_id(object.data["id"])
24 assert object == found_object
27 describe "generic changeset" do
28 test "it ensures uniqueness of the id" do
29 object = insert(:note)
30 cs = Object.change(%Object{}, %{data: %{id: object.data["id"]}})
33 {:error, _result} = Repo.insert(cs)
37 describe "deletion function" do
38 test "deletes an object" do
39 object = insert(:note)
40 found_object = Object.get_by_ap_id(object.data["id"])
42 assert object == found_object
44 Object.delete(found_object)
46 found_object = Object.get_by_ap_id(object.data["id"])
48 refute object == found_object
50 assert found_object.data["type"] == "Tombstone"
53 test "ensures cache is cleared for the object" do
54 object = insert(:note)
55 cached_object = Object.get_cached_by_ap_id(object.data["id"])
57 assert object == cached_object
59 Cachex.put(:web_resp_cache, URI.parse(object.data["id"]).path, "cofe")
61 Object.delete(cached_object)
63 {:ok, nil} = Cachex.get(:object_cache, "object:#{object.data["id"]}")
64 {:ok, nil} = Cachex.get(:web_resp_cache, URI.parse(object.data["id"]).path)
66 cached_object = Object.get_cached_by_ap_id(object.data["id"])
68 refute object == cached_object
70 assert cached_object.data["type"] == "Tombstone"
74 describe "delete attachments" do
75 clear_config([Pleroma.Upload])
77 test "in subdirectories" do
78 Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local)
81 content_type: "image/jpg",
82 path: Path.absname("test/fixtures/image.jpg"),
83 filename: "an_image.jpg"
88 {:ok, %Object{} = attachment} =
89 Pleroma.Web.ActivityPub.ActivityPub.upload(file, actor: user.ap_id)
91 %{data: %{"attachment" => [%{"url" => [%{"href" => href}]}]}} =
92 note = insert(:note, %{user: user, data: %{"attachment" => [attachment.data]}})
94 uploads_dir = Pleroma.Config.get!([Pleroma.Uploaders.Local, :uploads])
96 path = href |> Path.dirname() |> Path.basename()
98 assert {:ok, ["an_image.jpg"]} == File.ls("#{uploads_dir}/#{path}")
102 assert Object.get_by_id(attachment.id) == nil
104 assert {:ok, []} == File.ls("#{uploads_dir}/#{path}")
107 test "with dedupe enabled" do
108 Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local)
109 Pleroma.Config.put([Pleroma.Upload, :filters], [Pleroma.Upload.Filter.Dedupe])
111 uploads_dir = Pleroma.Config.get!([Pleroma.Uploaders.Local, :uploads])
113 File.mkdir_p!(uploads_dir)
116 content_type: "image/jpg",
117 path: Path.absname("test/fixtures/image.jpg"),
118 filename: "an_image.jpg"
123 {:ok, %Object{} = attachment} =
124 Pleroma.Web.ActivityPub.ActivityPub.upload(file, actor: user.ap_id)
126 %{data: %{"attachment" => [%{"url" => [%{"href" => href}]}]}} =
127 note = insert(:note, %{user: user, data: %{"attachment" => [attachment.data]}})
129 filename = Path.basename(href)
131 assert {:ok, files} = File.ls(uploads_dir)
132 assert filename in files
136 assert Object.get_by_id(attachment.id) == nil
137 assert {:ok, files} = File.ls(uploads_dir)
138 refute filename in files
142 describe "normalizer" do
143 test "fetches unknown objects by default" do
145 object = Object.normalize("http://mastodon.example.org/@admin/99541947525187367")
147 assert object.data["url"] == "http://mastodon.example.org/@admin/99541947525187367"
150 test "fetches unknown objects when fetch_remote is explicitly true" do
152 object = Object.normalize("http://mastodon.example.org/@admin/99541947525187367", true)
154 assert object.data["url"] == "http://mastodon.example.org/@admin/99541947525187367"
157 test "does not fetch unknown objects when fetch_remote is false" do
159 Object.normalize("http://mastodon.example.org/@admin/99541947525187367", false)
164 describe "get_by_id_and_maybe_refetch" do
167 %{method: :get, url: "https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d"} ->
168 %Tesla.Env{status: 200, body: File.read!("test/fixtures/tesla_mock/poll_original.json")}
171 apply(HttpRequestMock, :request, [env])
174 mock_modified = fn resp ->
176 %{method: :get, url: "https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d"} ->
180 apply(HttpRequestMock, :request, [env])
184 on_exit(fn -> mock(fn env -> apply(HttpRequestMock, :request, [env]) end) end)
186 [mock_modified: mock_modified]
189 test "refetches if the time since the last refetch is greater than the interval", %{
190 mock_modified: mock_modified
193 object = Object.normalize("https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d")
195 Object.set_cache(object)
197 assert Enum.at(object.data["oneOf"], 0)["replies"]["totalItems"] == 4
198 assert Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 0
200 mock_modified.(%Tesla.Env{
202 body: File.read!("test/fixtures/tesla_mock/poll_modified.json")
205 updated_object = Object.get_by_id_and_maybe_refetch(object.id, interval: -1)
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"] == 8
209 assert Enum.at(updated_object.data["oneOf"], 1)["replies"]["totalItems"] == 3
212 test "returns the old object if refetch fails", %{mock_modified: mock_modified} do
214 object = Object.normalize("https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d")
216 Object.set_cache(object)
218 assert Enum.at(object.data["oneOf"], 0)["replies"]["totalItems"] == 4
219 assert Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 0
221 assert capture_log(fn ->
222 mock_modified.(%Tesla.Env{status: 404, body: ""})
224 updated_object = Object.get_by_id_and_maybe_refetch(object.id, interval: -1)
225 object_in_cache = Object.get_cached_by_ap_id(object.data["id"])
226 assert updated_object == object_in_cache
227 assert Enum.at(updated_object.data["oneOf"], 0)["replies"]["totalItems"] == 4
228 assert Enum.at(updated_object.data["oneOf"], 1)["replies"]["totalItems"] == 0
230 "[error] Couldn't refresh https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d"
233 test "does not refetch if the time since the last refetch is greater than the interval", %{
234 mock_modified: mock_modified
237 object = Object.normalize("https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d")
239 Object.set_cache(object)
241 assert Enum.at(object.data["oneOf"], 0)["replies"]["totalItems"] == 4
242 assert Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 0
244 mock_modified.(%Tesla.Env{
246 body: File.read!("test/fixtures/tesla_mock/poll_modified.json")
249 updated_object = Object.get_by_id_and_maybe_refetch(object.id, interval: 100)
250 object_in_cache = Object.get_cached_by_ap_id(object.data["id"])
251 assert updated_object == object_in_cache
252 assert Enum.at(updated_object.data["oneOf"], 0)["replies"]["totalItems"] == 4
253 assert Enum.at(updated_object.data["oneOf"], 1)["replies"]["totalItems"] == 0
256 test "preserves internal fields on refetch", %{mock_modified: mock_modified} do
258 object = Object.normalize("https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d")
260 Object.set_cache(object)
262 assert Enum.at(object.data["oneOf"], 0)["replies"]["totalItems"] == 4
263 assert Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 0
266 activity = Activity.get_create_by_object_ap_id(object.data["id"])
267 {:ok, _activity, object} = CommonAPI.favorite(activity.id, user)
269 assert object.data["like_count"] == 1
271 mock_modified.(%Tesla.Env{
273 body: File.read!("test/fixtures/tesla_mock/poll_modified.json")
276 updated_object = Object.get_by_id_and_maybe_refetch(object.id, interval: -1)
277 object_in_cache = Object.get_cached_by_ap_id(object.data["id"])
278 assert updated_object == object_in_cache
279 assert Enum.at(updated_object.data["oneOf"], 0)["replies"]["totalItems"] == 8
280 assert Enum.at(updated_object.data["oneOf"], 1)["replies"]["totalItems"] == 3
282 assert updated_object.data["like_count"] == 1