schedule activity expiration in Oban

This commit is contained in:
Alexander Strizhakov 2020-08-22 20:46:01 +03:00 committed by rinpatch
parent 0254696e30
commit 9bf1065a06
23 changed files with 229 additions and 386 deletions

View File

@ -544,7 +544,6 @@ config :pleroma, Oban,
], ],
plugins: [Oban.Plugins.Pruner], plugins: [Oban.Plugins.Pruner],
crontab: [ crontab: [
{"* * * * *", Pleroma.Workers.Cron.PurgeExpiredActivitiesWorker},
{"0 0 * * 0", Pleroma.Workers.Cron.DigestEmailsWorker}, {"0 0 * * 0", Pleroma.Workers.Cron.DigestEmailsWorker},
{"0 0 * * *", Pleroma.Workers.Cron.NewUsersDigestWorker} {"0 0 * * *", Pleroma.Workers.Cron.NewUsersDigestWorker}
] ]
@ -655,7 +654,7 @@ config :pleroma, :rate_limit,
account_confirmation_resend: {8_640_000, 5}, account_confirmation_resend: {8_640_000, 5},
ap_routes: {60_000, 15} ap_routes: {60_000, 15}
config :pleroma, Pleroma.ActivityExpiration, enabled: true config :pleroma, Pleroma.Workers.PurgeExpiredActivity, enabled: true
config :pleroma, Pleroma.Plugs.RemoteIp, enabled: true config :pleroma, Pleroma.Plugs.RemoteIp, enabled: true

View File

@ -2290,7 +2290,6 @@ config :pleroma, :config_description, [
type: {:list, :tuple}, type: {:list, :tuple},
description: "Settings for cron background jobs", description: "Settings for cron background jobs",
suggestions: [ suggestions: [
{"* * * * *", Pleroma.Workers.Cron.PurgeExpiredActivitiesWorker},
{"0 0 * * 0", Pleroma.Workers.Cron.DigestEmailsWorker}, {"0 0 * * 0", Pleroma.Workers.Cron.DigestEmailsWorker},
{"0 0 * * *", Pleroma.Workers.Cron.NewUsersDigestWorker} {"0 0 * * *", Pleroma.Workers.Cron.NewUsersDigestWorker}
] ]

View File

@ -133,8 +133,7 @@ defmodule Mix.Tasks.Pleroma.Database do
days = Pleroma.Config.get([:mrf_activity_expiration, :days], 365) days = Pleroma.Config.get([:mrf_activity_expiration, :days], 365)
Pleroma.Activity Pleroma.Activity
|> join(:left, [a], u in assoc(a, :expiration)) |> join(:inner, [a], o in Object,
|> join(:inner, [a, _u], o in Object,
on: on:
fragment( fragment(
"(?->>'id') = COALESCE((?)->'object'->> 'id', (?)->>'object')", "(?->>'id') = COALESCE((?)->'object'->> 'id', (?)->>'object')",
@ -144,14 +143,21 @@ defmodule Mix.Tasks.Pleroma.Database do
) )
) )
|> where(local: true) |> where(local: true)
|> where([a, u], is_nil(u))
|> where([a], fragment("(? ->> 'type'::text) = 'Create'", a.data)) |> where([a], fragment("(? ->> 'type'::text) = 'Create'", a.data))
|> where([_a, _u, o], fragment("?->>'type' = 'Note'", o.data)) |> where([_a, o], fragment("?->>'type' = 'Note'", o.data))
|> Pleroma.RepoStreamer.chunk_stream(100) |> Pleroma.RepoStreamer.chunk_stream(100)
|> Stream.each(fn activities -> |> Stream.each(fn activities ->
Enum.each(activities, fn activity -> Enum.each(activities, fn activity ->
expires_at = Timex.shift(activity.inserted_at, days: days) expires_at =
Pleroma.ActivityExpiration.create(activity, expires_at, false) activity.inserted_at
|> DateTime.from_naive!("Etc/UTC")
|> Timex.shift(days: days)
Pleroma.Workers.PurgeExpiredActivity.enqueue(%{
activity_id: activity.id,
expires_at: expires_at,
validate: false
})
end) end)
end) end)
|> Stream.run() |> Stream.run()

View File

@ -7,7 +7,6 @@ defmodule Pleroma.Activity do
alias Pleroma.Activity alias Pleroma.Activity
alias Pleroma.Activity.Queries alias Pleroma.Activity.Queries
alias Pleroma.ActivityExpiration
alias Pleroma.Bookmark alias Pleroma.Bookmark
alias Pleroma.Notification alias Pleroma.Notification
alias Pleroma.Object alias Pleroma.Object
@ -60,8 +59,6 @@ defmodule Pleroma.Activity do
# typical case. # typical case.
has_one(:object, Object, on_delete: :nothing, foreign_key: :id) has_one(:object, Object, on_delete: :nothing, foreign_key: :id)
has_one(:expiration, ActivityExpiration, on_delete: :delete_all)
timestamps() timestamps()
end end

View File

@ -1,74 +0,0 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.ActivityExpiration do
use Ecto.Schema
alias Pleroma.Activity
alias Pleroma.ActivityExpiration
alias Pleroma.Repo
import Ecto.Changeset
import Ecto.Query
@type t :: %__MODULE__{}
@min_activity_lifetime :timer.hours(1)
schema "activity_expirations" do
belongs_to(:activity, Activity, type: FlakeId.Ecto.CompatType)
field(:scheduled_at, :naive_datetime)
end
def changeset(%ActivityExpiration{} = expiration, attrs, validate_scheduled_at) do
expiration
|> cast(attrs, [:scheduled_at])
|> validate_required([:scheduled_at])
|> validate_scheduled_at(validate_scheduled_at)
end
def get_by_activity_id(activity_id) do
ActivityExpiration
|> where([exp], exp.activity_id == ^activity_id)
|> Repo.one()
end
def create(%Activity{} = activity, scheduled_at, validate_scheduled_at \\ true) do
%ActivityExpiration{activity_id: activity.id}
|> changeset(%{scheduled_at: scheduled_at}, validate_scheduled_at)
|> Repo.insert()
end
def due_expirations(offset \\ 0) do
naive_datetime =
NaiveDateTime.utc_now()
|> NaiveDateTime.add(offset, :millisecond)
ActivityExpiration
|> where([exp], exp.scheduled_at < ^naive_datetime)
|> limit(50)
|> preload(:activity)
|> Repo.all()
|> Enum.reject(fn %{activity: activity} ->
Activity.pinned_by_actor?(activity)
end)
end
def validate_scheduled_at(changeset, false), do: changeset
def validate_scheduled_at(changeset, true) do
validate_change(changeset, :scheduled_at, fn _, scheduled_at ->
if not expires_late_enough?(scheduled_at) do
[scheduled_at: "an ephemeral activity must live for at least one hour"]
else
[]
end
end)
end
def expires_late_enough?(scheduled_at) do
now = NaiveDateTime.utc_now()
diff = NaiveDateTime.diff(scheduled_at, now, :millisecond)
diff > @min_activity_lifetime
end
end

View File

@ -5,7 +5,6 @@
defmodule Pleroma.Web.ActivityPub.ActivityPub do defmodule Pleroma.Web.ActivityPub.ActivityPub do
alias Pleroma.Activity alias Pleroma.Activity
alias Pleroma.Activity.Ir.Topics alias Pleroma.Activity.Ir.Topics
alias Pleroma.ActivityExpiration
alias Pleroma.Config alias Pleroma.Config
alias Pleroma.Constants alias Pleroma.Constants
alias Pleroma.Conversation alias Pleroma.Conversation
@ -165,7 +164,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end end
defp maybe_create_activity_expiration({:ok, %{data: %{"expires_at" => expires_at}} = activity}) do defp maybe_create_activity_expiration({:ok, %{data: %{"expires_at" => expires_at}} = activity}) do
with {:ok, _} <- ActivityExpiration.create(activity, expires_at) do with {:ok, _job} <-
Pleroma.Workers.PurgeExpiredActivity.enqueue(%{
activity_id: activity.id,
expires_at: expires_at
}) do
{:ok, activity} {:ok, activity}
end end
end end

View File

@ -31,10 +31,10 @@ defmodule Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy do
defp maybe_add_expiration(activity) do defp maybe_add_expiration(activity) do
days = Pleroma.Config.get([:mrf_activity_expiration, :days], 365) days = Pleroma.Config.get([:mrf_activity_expiration, :days], 365)
expires_at = NaiveDateTime.utc_now() |> Timex.shift(days: days) expires_at = DateTime.utc_now() |> Timex.shift(days: days)
with %{"expires_at" => existing_expires_at} <- activity, with %{"expires_at" => existing_expires_at} <- activity,
:lt <- NaiveDateTime.compare(existing_expires_at, expires_at) do :lt <- DateTime.compare(existing_expires_at, expires_at) do
activity activity
else else
_ -> Map.put(activity, "expires_at", expires_at) _ -> Map.put(activity, "expires_at", expires_at)

View File

@ -7,7 +7,6 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
""" """
alias Pleroma.Activity alias Pleroma.Activity
alias Pleroma.Activity.Ir.Topics alias Pleroma.Activity.Ir.Topics
alias Pleroma.ActivityExpiration
alias Pleroma.Chat alias Pleroma.Chat
alias Pleroma.Chat.MessageReference alias Pleroma.Chat.MessageReference
alias Pleroma.FollowingRelationship alias Pleroma.FollowingRelationship
@ -189,7 +188,10 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
end end
if expires_at = activity.data["expires_at"] do if expires_at = activity.data["expires_at"] do
ActivityExpiration.create(activity, expires_at) Pleroma.Workers.PurgeExpiredActivity.enqueue(%{
activity_id: activity.id,
expires_at: expires_at
})
end end
BackgroundWorker.enqueue("fetch_data_for_activity", %{"activity_id" => activity.id}) BackgroundWorker.enqueue("fetch_data_for_activity", %{"activity_id" => activity.id})

View File

@ -202,7 +202,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
additional = additional =
case draft.expires_at do case draft.expires_at do
%NaiveDateTime{} = expires_at -> Map.put(additional, "expires_at", expires_at) %DateTime{} = expires_at -> Map.put(additional, "expires_at", expires_at)
_ -> additional _ -> additional
end end

View File

@ -4,7 +4,6 @@
defmodule Pleroma.Web.CommonAPI do defmodule Pleroma.Web.CommonAPI do
alias Pleroma.Activity alias Pleroma.Activity
alias Pleroma.ActivityExpiration
alias Pleroma.Conversation.Participation alias Pleroma.Conversation.Participation
alias Pleroma.Formatter alias Pleroma.Formatter
alias Pleroma.Object alias Pleroma.Object
@ -381,9 +380,9 @@ defmodule Pleroma.Web.CommonAPI do
def check_expiry_date({:ok, nil} = res), do: res def check_expiry_date({:ok, nil} = res), do: res
def check_expiry_date({:ok, in_seconds}) do def check_expiry_date({:ok, in_seconds}) do
expiry = NaiveDateTime.utc_now() |> NaiveDateTime.add(in_seconds) expiry = DateTime.add(DateTime.utc_now(), in_seconds)
if ActivityExpiration.expires_late_enough?(expiry) do if Pleroma.Workers.PurgeExpiredActivity.expires_late_enough?(expiry) do
{:ok, expiry} {:ok, expiry}
else else
{:error, "Expiry date is too soon"} {:error, "Expiry date is too soon"}

View File

@ -8,7 +8,6 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
require Pleroma.Constants require Pleroma.Constants
alias Pleroma.Activity alias Pleroma.Activity
alias Pleroma.ActivityExpiration
alias Pleroma.HTML alias Pleroma.HTML
alias Pleroma.Object alias Pleroma.Object
alias Pleroma.Repo alias Pleroma.Repo
@ -245,8 +244,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
expires_at = expires_at =
with true <- client_posted_this_activity, with true <- client_posted_this_activity,
%ActivityExpiration{scheduled_at: scheduled_at} <- %Oban.Job{scheduled_at: scheduled_at} <-
ActivityExpiration.get_by_activity_id(activity.id) do Pleroma.Workers.PurgeExpiredActivity.get_expiration(activity.id) do
scheduled_at scheduled_at
else else
_ -> nil _ -> nil

View File

@ -1,48 +0,0 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Workers.Cron.PurgeExpiredActivitiesWorker do
@moduledoc """
The worker to purge expired activities.
"""
use Oban.Worker, queue: "activity_expiration"
alias Pleroma.Activity
alias Pleroma.ActivityExpiration
alias Pleroma.Config
alias Pleroma.User
alias Pleroma.Web.CommonAPI
require Logger
@interval :timer.minutes(1)
@impl Oban.Worker
def perform(_job) do
if Config.get([ActivityExpiration, :enabled]) do
Enum.each(ActivityExpiration.due_expirations(@interval), &delete_activity/1)
end
after
:ok
end
def delete_activity(%ActivityExpiration{activity_id: activity_id}) do
with {:activity, %Activity{} = activity} <-
{:activity, Activity.get_by_id_with_object(activity_id)},
{:user, %User{} = user} <- {:user, User.get_by_ap_id(activity.object.data["actor"])} do
CommonAPI.delete(activity.id, user)
else
{:activity, _} ->
Logger.error(
"#{__MODULE__} Couldn't delete expired activity: not found activity ##{activity_id}"
)
{:user, _} ->
Logger.error(
"#{__MODULE__} Couldn't delete expired activity: not found actor of ##{activity_id}"
)
end
end
end

View File

@ -0,0 +1,72 @@
defmodule Pleroma.Workers.PurgeExpiredActivity do
@moduledoc """
Worker which purges expired activity.
"""
use Oban.Worker, queue: :activity_expiration, max_attempts: 1
import Ecto.Query
def enqueue(args) do
with true <- enabled?(),
args when is_map(args) <- validate_expires_at(args) do
{scheduled_at, args} = Map.pop(args, :expires_at)
args
|> __MODULE__.new(scheduled_at: scheduled_at)
|> Oban.insert()
end
end
@impl true
def perform(%Oban.Job{args: %{"activity_id" => id}}) do
with %Pleroma.Activity{} = activity <- find_activity(id),
%Pleroma.User{} = user <- find_user(activity.object.data["actor"]) do
Pleroma.Web.CommonAPI.delete(activity.id, user)
end
end
defp enabled? do
with false <- Pleroma.Config.get([__MODULE__, :enabled], false) do
{:error, :expired_activities_disabled}
end
end
defp validate_expires_at(%{validate: false} = args), do: Map.delete(args, :validate)
defp validate_expires_at(args) do
if expires_late_enough?(args[:expires_at]) do
args
else
{:error, :expiration_too_close}
end
end
defp find_activity(id) do
with nil <- Pleroma.Activity.get_by_id_with_object(id) do
{:error, :activity_not_found}
end
end
defp find_user(ap_id) do
with nil <- Pleroma.User.get_by_ap_id(ap_id) do
{:error, :user_not_found}
end
end
def get_expiration(id) do
from(j in Oban.Job,
where: j.state == "scheduled",
where: j.queue == "activity_expiration",
where: fragment("?->>'activity_id' = ?", j.args, ^id)
)
|> Pleroma.Repo.one()
end
@spec expires_late_enough?(DateTime.t()) :: boolean()
def expires_late_enough?(scheduled_at) do
now = DateTime.utc_now()
diff = DateTime.diff(scheduled_at, now, :millisecond)
diff > :timer.hours(1)
end
end

View File

@ -1,55 +0,0 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.ActivityExpirationTest do
use Pleroma.DataCase
alias Pleroma.ActivityExpiration
import Pleroma.Factory
setup do: clear_config([ActivityExpiration, :enabled])
test "finds activities due to be deleted only" do
activity = insert(:note_activity)
expiration_due =
insert(:expiration_in_the_past, %{activity_id: activity.id}) |> Repo.preload(:activity)
activity2 = insert(:note_activity)
insert(:expiration_in_the_future, %{activity_id: activity2.id})
expirations = ActivityExpiration.due_expirations()
assert length(expirations) == 1
assert hd(expirations) == expiration_due
end
test "denies expirations that don't live long enough" do
activity = insert(:note_activity)
now = NaiveDateTime.utc_now()
assert {:error, _} = ActivityExpiration.create(activity, now)
end
test "deletes an expiration activity" do
Pleroma.Config.put([ActivityExpiration, :enabled], true)
activity = insert(:note_activity)
naive_datetime =
NaiveDateTime.add(
NaiveDateTime.utc_now(),
-:timer.minutes(2),
:millisecond
)
expiration =
insert(
:expiration_in_the_past,
%{activity_id: activity.id, scheduled_at: naive_datetime}
)
Pleroma.Workers.Cron.PurgeExpiredActivitiesWorker.perform(%Oban.Job{})
refute Pleroma.Repo.get(Pleroma.Activity, activity.id)
refute Pleroma.Repo.get(Pleroma.ActivityExpiration, expiration.id)
end
end

View File

@ -185,15 +185,6 @@ defmodule Pleroma.ActivityTest do
end end
end end
test "add an activity with an expiration" do
activity = insert(:note_activity)
insert(:expiration_in_the_future, %{activity_id: activity.id})
Pleroma.ActivityExpiration
|> where([a], a.activity_id == ^activity.id)
|> Repo.one!()
end
test "all_by_ids_with_object/1" do test "all_by_ids_with_object/1" do
%{id: id1} = insert(:note_activity) %{id: id1} = insert(:note_activity)
%{id: id2} = insert(:note_activity) %{id: id2} = insert(:note_activity)

View File

@ -200,25 +200,6 @@ defmodule Pleroma.Factory do
|> Map.merge(attrs) |> Map.merge(attrs)
end end
defp expiration_offset_by_minutes(attrs, minutes) do
scheduled_at =
NaiveDateTime.utc_now()
|> NaiveDateTime.add(:timer.minutes(minutes), :millisecond)
|> NaiveDateTime.truncate(:second)
%Pleroma.ActivityExpiration{}
|> Map.merge(attrs)
|> Map.put(:scheduled_at, scheduled_at)
end
def expiration_in_the_past_factory(attrs \\ %{}) do
expiration_offset_by_minutes(attrs, -60)
end
def expiration_in_the_future_factory(attrs \\ %{}) do
expiration_offset_by_minutes(attrs, 61)
end
def article_activity_factory do def article_activity_factory do
article = insert(:article) article = insert(:article)

View File

@ -3,14 +3,15 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Mix.Tasks.Pleroma.DatabaseTest do defmodule Mix.Tasks.Pleroma.DatabaseTest do
use Pleroma.DataCase
use Oban.Testing, repo: Pleroma.Repo
alias Pleroma.Activity alias Pleroma.Activity
alias Pleroma.Object alias Pleroma.Object
alias Pleroma.Repo alias Pleroma.Repo
alias Pleroma.User alias Pleroma.User
alias Pleroma.Web.CommonAPI alias Pleroma.Web.CommonAPI
use Pleroma.DataCase
import Pleroma.Factory import Pleroma.Factory
setup_all do setup_all do
@ -130,40 +131,45 @@ defmodule Mix.Tasks.Pleroma.DatabaseTest do
describe "ensure_expiration" do describe "ensure_expiration" do
test "it adds to expiration old statuses" do test "it adds to expiration old statuses" do
%{id: activity_id1} = insert(:note_activity) activity1 = insert(:note_activity)
%{id: activity_id2} = {:ok, inserted_at, 0} = DateTime.from_iso8601("2015-01-23T23:50:07Z")
insert(:note_activity, %{inserted_at: NaiveDateTime.from_iso8601!("2015-01-23 23:50:07")}) activity2 = insert(:note_activity, %{inserted_at: inserted_at})
%{id: activity_id3} = activity3 = insert(:note_activity) %{id: activity_id3} = insert(:note_activity)
expires_at = expires_at = DateTime.add(DateTime.utc_now(), 60 * 61)
NaiveDateTime.utc_now()
|> NaiveDateTime.add(60 * 61, :second)
|> NaiveDateTime.truncate(:second)
Pleroma.ActivityExpiration.create(activity3, expires_at) Pleroma.Workers.PurgeExpiredActivity.enqueue(%{
activity_id: activity_id3,
expires_at: expires_at
})
Mix.Tasks.Pleroma.Database.run(["ensure_expiration"]) Mix.Tasks.Pleroma.Database.run(["ensure_expiration"])
expirations = assert_enqueued(
Pleroma.ActivityExpiration worker: Pleroma.Workers.PurgeExpiredActivity,
|> order_by(:activity_id) args: %{activity_id: activity1.id},
|> Repo.all() scheduled_at:
activity1.inserted_at
|> DateTime.from_naive!("Etc/UTC")
|> Timex.shift(days: 365)
)
assert [ assert_enqueued(
%Pleroma.ActivityExpiration{ worker: Pleroma.Workers.PurgeExpiredActivity,
activity_id: ^activity_id1 args: %{activity_id: activity2.id},
}, scheduled_at:
%Pleroma.ActivityExpiration{ activity2.inserted_at
activity_id: ^activity_id2, |> DateTime.from_naive!("Etc/UTC")
scheduled_at: ~N[2016-01-23 23:50:07] |> Timex.shift(days: 365)
}, )
%Pleroma.ActivityExpiration{
activity_id: ^activity_id3, assert_enqueued(
scheduled_at: ^expires_at worker: Pleroma.Workers.PurgeExpiredActivity,
} args: %{activity_id: activity_id3},
] = expirations scheduled_at: expires_at
)
end end
end end
end end

View File

@ -2069,18 +2069,25 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
end end
describe "global activity expiration" do describe "global activity expiration" do
setup do: clear_config([:mrf, :policies])
test "creates an activity expiration for local Create activities" do test "creates an activity expiration for local Create activities" do
Pleroma.Config.put( clear_config([:mrf, :policies], Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy)
[:mrf, :policies],
Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy {:ok, activity} = ActivityBuilder.insert(%{"type" => "Create", "context" => "3hu"})
{:ok, follow} = ActivityBuilder.insert(%{"type" => "Follow", "context" => "3hu"})
assert_enqueued(
worker: Pleroma.Workers.PurgeExpiredActivity,
args: %{activity_id: activity.id},
scheduled_at:
activity.inserted_at
|> DateTime.from_naive!("Etc/UTC")
|> Timex.shift(days: 365)
) )
{:ok, %{id: id_create}} = ActivityBuilder.insert(%{"type" => "Create", "context" => "3hu"}) refute_enqueued(
{:ok, _follow} = ActivityBuilder.insert(%{"type" => "Follow", "context" => "3hu"}) worker: Pleroma.Workers.PurgeExpiredActivity,
args: %{activity_id: follow.id}
assert [%{activity_id: ^id_create}] = Pleroma.ActivityExpiration |> Repo.all() )
end end
end end

View File

@ -18,11 +18,11 @@ defmodule Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicyTest do
"object" => %{"type" => "Note"} "object" => %{"type" => "Note"}
}) })
assert Timex.diff(expires_at, NaiveDateTime.utc_now(), :days) == 364 assert Timex.diff(expires_at, DateTime.utc_now(), :days) == 364
end end
test "keeps existing `expires_at` if it less than the config setting" do test "keeps existing `expires_at` if it less than the config setting" do
expires_at = NaiveDateTime.utc_now() |> Timex.shift(days: 1) expires_at = DateTime.utc_now() |> Timex.shift(days: 1)
assert {:ok, %{"type" => "Create", "expires_at" => ^expires_at}} = assert {:ok, %{"type" => "Create", "expires_at" => ^expires_at}} =
ActivityExpirationPolicy.filter(%{ ActivityExpirationPolicy.filter(%{
@ -35,7 +35,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicyTest do
end end
test "overwrites existing `expires_at` if it greater than the config setting" do test "overwrites existing `expires_at` if it greater than the config setting" do
too_distant_future = NaiveDateTime.utc_now() |> Timex.shift(years: 2) too_distant_future = DateTime.utc_now() |> Timex.shift(years: 2)
assert {:ok, %{"type" => "Create", "expires_at" => expires_at}} = assert {:ok, %{"type" => "Create", "expires_at" => expires_at}} =
ActivityExpirationPolicy.filter(%{ ActivityExpirationPolicy.filter(%{
@ -46,7 +46,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicyTest do
"object" => %{"type" => "Note"} "object" => %{"type" => "Note"}
}) })
assert Timex.diff(expires_at, NaiveDateTime.utc_now(), :days) == 364 assert Timex.diff(expires_at, DateTime.utc_now(), :days) == 364
end end
test "ignores remote activities" do test "ignores remote activities" do

View File

@ -4,6 +4,8 @@
defmodule Pleroma.Web.CommonAPITest do defmodule Pleroma.Web.CommonAPITest do
use Pleroma.DataCase use Pleroma.DataCase
use Oban.Testing, repo: Pleroma.Repo
alias Pleroma.Activity alias Pleroma.Activity
alias Pleroma.Chat alias Pleroma.Chat
alias Pleroma.Conversation.Participation alias Pleroma.Conversation.Participation
@ -598,15 +600,15 @@ defmodule Pleroma.Web.CommonAPITest do
test "it can handle activities that expire" do test "it can handle activities that expire" do
user = insert(:user) user = insert(:user)
expires_at = expires_at = DateTime.add(DateTime.utc_now(), 1_000_000)
NaiveDateTime.utc_now()
|> NaiveDateTime.truncate(:second)
|> NaiveDateTime.add(1_000_000, :second)
assert {:ok, activity} = CommonAPI.post(user, %{status: "chai", expires_in: 1_000_000}) assert {:ok, activity} = CommonAPI.post(user, %{status: "chai", expires_in: 1_000_000})
assert expiration = Pleroma.ActivityExpiration.get_by_activity_id(activity.id) assert_enqueued(
assert expiration.scheduled_at == expires_at worker: Pleroma.Workers.PurgeExpiredActivity,
args: %{activity_id: activity.id},
scheduled_at: expires_at
)
end end
end end

View File

@ -4,9 +4,9 @@
defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
use Pleroma.Web.ConnCase use Pleroma.Web.ConnCase
use Oban.Testing, repo: Pleroma.Repo
alias Pleroma.Activity alias Pleroma.Activity
alias Pleroma.ActivityExpiration
alias Pleroma.Config alias Pleroma.Config
alias Pleroma.Conversation.Participation alias Pleroma.Conversation.Participation
alias Pleroma.Object alias Pleroma.Object
@ -29,8 +29,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
setup do: oauth_access(["write:statuses"]) setup do: oauth_access(["write:statuses"])
test "posting a status does not increment reblog_count when relaying", %{conn: conn} do test "posting a status does not increment reblog_count when relaying", %{conn: conn} do
Pleroma.Config.put([:instance, :federating], true) Config.put([:instance, :federating], true)
Pleroma.Config.get([:instance, :allow_relay], true) Config.get([:instance, :allow_relay], true)
response = response =
conn conn
@ -103,7 +103,9 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
# An activity that will expire: # An activity that will expire:
# 2 hours # 2 hours
expires_in = 120 * 60 expires_in = 2 * 60 * 60
expires_at = DateTime.add(DateTime.utc_now(), expires_in)
conn_four = conn_four =
conn conn
@ -116,19 +118,13 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
assert fourth_response = assert fourth_response =
%{"id" => fourth_id} = json_response_and_validate_schema(conn_four, 200) %{"id" => fourth_id} = json_response_and_validate_schema(conn_four, 200)
assert activity = Activity.get_by_id(fourth_id) assert Activity.get_by_id(fourth_id)
assert expiration = ActivityExpiration.get_by_activity_id(fourth_id)
estimated_expires_at = assert_enqueued(
NaiveDateTime.utc_now() worker: Pleroma.Workers.PurgeExpiredActivity,
|> NaiveDateTime.add(expires_in) args: %{activity_id: fourth_id},
|> NaiveDateTime.truncate(:second) scheduled_at: expires_at
)
# This assert will fail if the test takes longer than a minute. I sure hope it never does:
assert abs(NaiveDateTime.diff(expiration.scheduled_at, estimated_expires_at, :second)) < 60
assert fourth_response["pleroma"]["expires_at"] ==
NaiveDateTime.to_iso8601(expiration.scheduled_at)
end end
test "it fails to create a status if `expires_in` is less or equal than an hour", %{ test "it fails to create a status if `expires_in` is less or equal than an hour", %{
@ -160,8 +156,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
end end
test "Get MRF reason when posting a status is rejected by one", %{conn: conn} do test "Get MRF reason when posting a status is rejected by one", %{conn: conn} do
Pleroma.Config.put([:mrf_keyword, :reject], ["GNO"]) Config.put([:mrf_keyword, :reject], ["GNO"])
Pleroma.Config.put([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.KeywordPolicy]) Config.put([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.KeywordPolicy])
assert %{"error" => "[KeywordPolicy] Matches with rejected keyword"} = assert %{"error" => "[KeywordPolicy] Matches with rejected keyword"} =
conn conn
@ -1681,19 +1677,17 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
test "expires_at is nil for another user" do test "expires_at is nil for another user" do
%{conn: conn, user: user} = oauth_access(["read:statuses"]) %{conn: conn, user: user} = oauth_access(["read:statuses"])
expires_at = DateTime.add(DateTime.utc_now(), 1_000_000)
{:ok, activity} = CommonAPI.post(user, %{status: "foobar", expires_in: 1_000_000}) {:ok, activity} = CommonAPI.post(user, %{status: "foobar", expires_in: 1_000_000})
expires_at = assert %{"pleroma" => %{"expires_at" => a_expires_at}} =
activity.id
|> ActivityExpiration.get_by_activity_id()
|> Map.get(:scheduled_at)
|> NaiveDateTime.to_iso8601()
assert %{"pleroma" => %{"expires_at" => ^expires_at}} =
conn conn
|> get("/api/v1/statuses/#{activity.id}") |> get("/api/v1/statuses/#{activity.id}")
|> json_response_and_validate_schema(:ok) |> json_response_and_validate_schema(:ok)
{:ok, a_expires_at, 0} = DateTime.from_iso8601(a_expires_at)
assert DateTime.diff(expires_at, a_expires_at) == 0
%{conn: conn} = oauth_access(["read:statuses"]) %{conn: conn} = oauth_access(["read:statuses"])
assert %{"pleroma" => %{"expires_at" => nil}} = assert %{"pleroma" => %{"expires_at" => nil}} =

View File

@ -1,84 +0,0 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Workers.Cron.PurgeExpiredActivitiesWorkerTest do
use Pleroma.DataCase
alias Pleroma.ActivityExpiration
alias Pleroma.Workers.Cron.PurgeExpiredActivitiesWorker
import Pleroma.Factory
import ExUnit.CaptureLog
setup do
clear_config([ActivityExpiration, :enabled])
end
test "deletes an expiration activity" do
Pleroma.Config.put([ActivityExpiration, :enabled], true)
activity = insert(:note_activity)
naive_datetime =
NaiveDateTime.add(
NaiveDateTime.utc_now(),
-:timer.minutes(2),
:millisecond
)
expiration =
insert(
:expiration_in_the_past,
%{activity_id: activity.id, scheduled_at: naive_datetime}
)
Pleroma.Workers.Cron.PurgeExpiredActivitiesWorker.perform(%Oban.Job{})
refute Pleroma.Repo.get(Pleroma.Activity, activity.id)
refute Pleroma.Repo.get(Pleroma.ActivityExpiration, expiration.id)
end
test "works with ActivityExpirationPolicy" do
Pleroma.Config.put([ActivityExpiration, :enabled], true)
clear_config([:mrf, :policies], Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy)
user = insert(:user)
days = Pleroma.Config.get([:mrf_activity_expiration, :days], 365)
{:ok, %{id: id} = activity} = Pleroma.Web.CommonAPI.post(user, %{status: "cofe"})
past_date =
NaiveDateTime.utc_now() |> Timex.shift(days: -days) |> NaiveDateTime.truncate(:second)
activity
|> Repo.preload(:expiration)
|> Map.get(:expiration)
|> Ecto.Changeset.change(%{scheduled_at: past_date})
|> Repo.update!()
Pleroma.Workers.Cron.PurgeExpiredActivitiesWorker.perform(%Oban.Job{})
assert [%{data: %{"type" => "Delete", "deleted_activity_id" => ^id}}] =
Pleroma.Repo.all(Pleroma.Activity)
end
describe "delete_activity/1" do
test "adds log message if activity isn't find" do
assert capture_log([level: :error], fn ->
PurgeExpiredActivitiesWorker.delete_activity(%ActivityExpiration{
activity_id: "test-activity"
})
end) =~ "Couldn't delete expired activity: not found activity"
end
test "adds log message if actor isn't find" do
assert capture_log([level: :error], fn ->
PurgeExpiredActivitiesWorker.delete_activity(%ActivityExpiration{
activity_id: "test-activity"
})
end) =~ "Couldn't delete expired activity: not found activity"
end
end
end

View File

@ -0,0 +1,47 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Workers.PurgeExpiredActivityTest do
use Pleroma.DataCase, async: true
use Oban.Testing, repo: Pleroma.Repo
import Pleroma.Factory
alias Pleroma.Workers.PurgeExpiredActivity
test "denies expirations that don't live long enough" do
activity = insert(:note_activity)
assert {:error, :expiration_too_close} =
PurgeExpiredActivity.enqueue(%{
activity_id: activity.id,
expires_at: DateTime.utc_now()
})
refute_enqueued(
worker: Pleroma.Workers.PurgeExpiredActivity,
args: %{activity_id: activity.id}
)
end
test "enqueue job" do
activity = insert(:note_activity)
assert {:ok, _} =
PurgeExpiredActivity.enqueue(%{
activity_id: activity.id,
expires_at: DateTime.add(DateTime.utc_now(), 3601)
})
assert_enqueued(
worker: Pleroma.Workers.PurgeExpiredActivity,
args: %{activity_id: activity.id}
)
assert {:ok, _} =
perform_job(Pleroma.Workers.PurgeExpiredActivity, %{activity_id: activity.id})
assert %Oban.Job{} = Pleroma.Workers.PurgeExpiredActivity.get_expiration(activity.id)
end
end