Page MenuHomePhorge

No OneTemporary

Size
22 KB
Referenced Files
None
Subscribers
None
diff --git a/config/test.exs b/config/test.exs
index 6fe84478a..6bba555b0 100644
--- a/config/test.exs
+++ b/config/test.exs
@@ -1,199 +1,200 @@
import Config
# We don't run a server during test. If one is required,
# you can enable the server option below.
config :pleroma, Pleroma.Web.Endpoint,
http: [port: 4001],
url: [port: 4001],
server: true
# Disable captha for tests
config :pleroma, Pleroma.Captcha,
# It should not be enabled for automatic tests
enabled: false,
# A fake captcha service for tests
method: Pleroma.Captcha.Mock
# Print only warnings and errors during test
config :logger, :console,
level: :warning,
format: "\n[$level] $message\n"
config :pleroma, :auth, oauth_consumer_strategies: []
config :pleroma, Pleroma.Upload,
filters: [],
link_name: false,
default_description: :filename
config :pleroma, Pleroma.Uploaders.Local, uploads: "test/uploads"
config :pleroma, Pleroma.Emails.Mailer, adapter: Swoosh.Adapters.Test, enabled: true
config :pleroma, :instance,
email: "admin@example.com",
notify_email: "noreply@example.com",
skip_thread_containment: false,
federating: false,
external_user_synchronization: false,
static_dir: "test/instance_static/"
config :pleroma, :activitypub, sign_object_fetches: false, follow_handshake_timeout: 0
# Configure your database
config :pleroma, Pleroma.Repo,
adapter: Ecto.Adapters.Postgres,
username: "postgres",
password: "postgres",
database: "pleroma_test",
hostname: System.get_env("DB_HOST") || "localhost",
port: System.get_env("DB_PORT") || "5432",
pool: Ecto.Adapters.SQL.Sandbox,
pool_size: System.schedulers_online() * 2,
log: false
config :pleroma, :dangerzone, override_repo_pool_size: true
# Reduce hash rounds for testing
config :pleroma, :password, iterations: 1
config :tesla, adapter: Tesla.Mock
config :pleroma, :rich_media,
enabled: false,
ignore_hosts: [],
ignore_tld: ["local", "localdomain", "lan"],
max_body: 2_000_000
config :pleroma, :instance,
multi_factor_authentication: [
totp: [
# digits 6 or 8
digits: 6,
period: 30
],
backup_codes: [
number: 2,
length: 6
]
]
config :web_push_encryption, :vapid_details,
subject: "mailto:administrator@example.com",
public_key:
"BLH1qVhJItRGCfxgTtONfsOKDc9VRAraXw-3NsmjMngWSh7NxOizN6bkuRA7iLTMPS82PjwJAr3UoK9EC1IFrz4",
private_key: "_-XZ0iebPrRfZ_o0-IatTdszYa8VCH1yLN-JauK7HHA"
config :pleroma, Oban, testing: :manual
config :pleroma, Pleroma.ScheduledActivity,
daily_user_limit: 2,
total_user_limit: 3,
enabled: false
config :pleroma, :rate_limit, %{}
config :pleroma, :http_security, report_uri: "https://endpoint.com"
config :pleroma, :http, send_user_agent: false
rum_enabled = System.get_env("RUM_ENABLED") == "true"
config :pleroma, :database, rum_enabled: rum_enabled
IO.puts("RUM enabled: #{rum_enabled}")
config :joken, default_signer: "yU8uHKq+yyAkZ11Hx//jcdacWc8yQ1bxAAGrplzB0Zwwjkp35v0RK9SO8WTPr6QZ"
config :pleroma, Pleroma.ReverseProxy.Client, Pleroma.ReverseProxy.ClientMock
config :pleroma, :modules, runtime_dir: "test/fixtures/modules"
config :pleroma, Pleroma.Gun, Pleroma.GunMock
config :pleroma, Pleroma.Emails.NewUsersDigestEmail, enabled: true
config :pleroma, Pleroma.Web.Plugs.RemoteIp, enabled: false
config :pleroma, Pleroma.Web.ApiSpec.CastAndValidate, strict: true
config :tzdata, :autoupdate, :disabled
config :pleroma, :mrf, policies: []
config :pleroma, :pipeline,
object_validator: Pleroma.Web.ActivityPub.ObjectValidatorMock,
mrf: Pleroma.Web.ActivityPub.MRFMock,
activity_pub: Pleroma.Web.ActivityPub.ActivityPubMock,
side_effects: Pleroma.Web.ActivityPub.SideEffectsMock,
federator: Pleroma.Web.FederatorMock,
config: Pleroma.ConfigMock
config :pleroma, :cachex, provider: Pleroma.CachexMock
config :pleroma, Pleroma.Web.WebFinger, update_nickname_on_user_fetch: false
config :pleroma, :side_effects,
ap_streamer: Pleroma.Web.ActivityPub.ActivityPubMock,
logger: Pleroma.LoggerMock
config :pleroma, Pleroma.Search, module: Pleroma.Search.DatabaseSearch
config :pleroma, Pleroma.Search.Meilisearch, url: "http://127.0.0.1:7700/", private_key: nil
# Reduce recompilation time
# https://dashbit.co/blog/speeding-up-re-compilation-of-elixir-projects
config :phoenix, :plug_init_mode, :runtime
config :pleroma, :config_impl, Pleroma.UnstubbedConfigMock
+config :pleroma, :datetime_impl, Pleroma.DateTimeMock
config :pleroma, Pleroma.PromEx, disabled: true
# Mox definitions. Only read during compile time.
config :pleroma, Pleroma.User.Backup, config_impl: Pleroma.UnstubbedConfigMock
config :pleroma, Pleroma.Uploaders.S3, ex_aws_impl: Pleroma.Uploaders.S3.ExAwsMock
config :pleroma, Pleroma.Uploaders.S3, config_impl: Pleroma.UnstubbedConfigMock
config :pleroma, Pleroma.Upload, config_impl: Pleroma.UnstubbedConfigMock
config :pleroma, Pleroma.ScheduledActivity, config_impl: Pleroma.UnstubbedConfigMock
config :pleroma, Pleroma.Web.RichMedia.Helpers, config_impl: Pleroma.StaticStubbedConfigMock
config :pleroma, Pleroma.Uploaders.IPFS, config_impl: Pleroma.UnstubbedConfigMock
config :pleroma, Pleroma.Web.Plugs.HTTPSecurityPlug, config_impl: Pleroma.StaticStubbedConfigMock
config :pleroma, Pleroma.Web.Plugs.HTTPSignaturePlug, config_impl: Pleroma.StaticStubbedConfigMock
config :pleroma, Pleroma.Signature, http_signatures_impl: Pleroma.StubbedHTTPSignaturesMock
peer_module =
if String.to_integer(System.otp_release()) >= 25 do
:peer
else
:slave
end
config :pleroma, Pleroma.Cluster, peer_module: peer_module
config :pleroma, Pleroma.Application,
background_migrators: false,
internal_fetch: false,
load_custom_modules: false,
max_restarts: 100,
streamer_registry: false,
test_http_pools: true
config :pleroma, Pleroma.Web.Streaming, sync_streaming: true
config :pleroma, Pleroma.Uploaders.Uploader, timeout: 1_000
config :pleroma, Pleroma.Emoji.Loader, test_emoji: true
config :pleroma, Pleroma.Web.RichMedia.Backfill,
stream_out: Pleroma.Web.ActivityPub.ActivityPubMock
config :pleroma, Pleroma.Web.Plugs.HTTPSecurityPlug, enable: false
config :pleroma, Pleroma.User.Backup, tempdir: "test/tmp"
if File.exists?("./config/test.secret.exs") do
import_config "test.secret.exs"
else
IO.puts(
"You may want to create test.secret.exs to declare custom database connection parameters."
)
end
diff --git a/lib/pleroma/datetime.ex b/lib/pleroma/datetime.ex
new file mode 100644
index 000000000..d79cb848b
--- /dev/null
+++ b/lib/pleroma/datetime.ex
@@ -0,0 +1,3 @@
+defmodule Pleroma.DateTime do
+ @callback utc_now() :: NaiveDateTime.t()
+end
diff --git a/lib/pleroma/datetime/impl.ex b/lib/pleroma/datetime/impl.ex
new file mode 100644
index 000000000..102be047b
--- /dev/null
+++ b/lib/pleroma/datetime/impl.ex
@@ -0,0 +1,6 @@
+defmodule Pleroma.DateTime.Impl do
+ @behaviour Pleroma.DateTime
+
+ @impl true
+ def utc_now, do: NaiveDateTime.utc_now()
+end
diff --git a/lib/pleroma/user_relationship.ex b/lib/pleroma/user_relationship.ex
index 82fcc1cdd..5b48d321a 100644
--- a/lib/pleroma/user_relationship.ex
+++ b/lib/pleroma/user_relationship.ex
@@ -1,244 +1,250 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.UserRelationship do
use Ecto.Schema
import Ecto.Changeset
import Ecto.Query
alias Ecto.Changeset
alias Pleroma.FollowingRelationship
alias Pleroma.Repo
alias Pleroma.User
alias Pleroma.UserRelationship
@type t :: %__MODULE__{}
schema "user_relationships" do
belongs_to(:source, User, type: FlakeId.Ecto.CompatType)
belongs_to(:target, User, type: FlakeId.Ecto.CompatType)
field(:relationship_type, Pleroma.UserRelationship.Type)
field(:expires_at, :utc_datetime)
timestamps(updated_at: false)
end
for relationship_type <- Keyword.keys(Pleroma.UserRelationship.Type.__enum_map__()) do
# `def create_block/3`, `def create_mute/3`, `def create_reblog_mute/3`,
# `def create_notification_mute/3`, `def create_inverse_subscription/3`,
# `def endorsement/3`
def unquote(:"create_#{relationship_type}")(source, target, expires_at \\ nil),
do: create(unquote(relationship_type), source, target, expires_at)
# `def delete_block/2`, `def delete_mute/2`, `def delete_reblog_mute/2`,
# `def delete_notification_mute/2`, `def delete_inverse_subscription/2`,
# `def delete_endorsement/2`
def unquote(:"delete_#{relationship_type}")(source, target),
do: delete(unquote(relationship_type), source, target)
# `def block_exists?/2`, `def mute_exists?/2`, `def reblog_mute_exists?/2`,
# `def notification_mute_exists?/2`, `def inverse_subscription_exists?/2`,
# `def inverse_endorsement_exists?/2`
def unquote(:"#{relationship_type}_exists?")(source, target),
do: exists?(unquote(relationship_type), source, target)
# `def get_block_expire_date/2`, `def get_mute_expire_date/2`,
# `def get_reblog_mute_expire_date/2`, `def get_notification_mute_exists?/2`,
# `def get_inverse_subscription_expire_date/2`, `def get_inverse_endorsement_expire_date/2`
def unquote(:"get_#{relationship_type}_expire_date")(source, target),
do: get_expire_date(unquote(relationship_type), source, target)
end
def user_relationship_types, do: Keyword.keys(user_relationship_mappings())
def user_relationship_mappings, do: Pleroma.UserRelationship.Type.__enum_map__()
+ def datetime_impl do
+ Application.get_env(:pleroma, :datetime_impl, Pleroma.DateTime.Impl)
+ end
+
def changeset(%UserRelationship{} = user_relationship, params \\ %{}) do
user_relationship
- |> cast(params, [:relationship_type, :source_id, :target_id, :expires_at])
+ |> cast(params, [:relationship_type, :source_id, :target_id, :expires_at, :inserted_at])
|> validate_required([:relationship_type, :source_id, :target_id])
|> unique_constraint(:relationship_type,
name: :user_relationships_source_id_relationship_type_target_id_index
)
|> validate_not_self_relationship()
end
+ @spec exists?(any(), Pleroma.User.t(), Pleroma.User.t()) :: boolean()
def exists?(relationship_type, %User{} = source, %User{} = target) do
UserRelationship
|> where(relationship_type: ^relationship_type, source_id: ^source.id, target_id: ^target.id)
|> Repo.exists?()
end
def get_expire_date(relationship_type, %User{} = source, %User{} = target) do
%UserRelationship{expires_at: expires_at} =
UserRelationship
|> where(
relationship_type: ^relationship_type,
source_id: ^source.id,
target_id: ^target.id
)
|> Repo.one!()
expires_at
end
def create(relationship_type, %User{} = source, %User{} = target, expires_at \\ nil) do
%UserRelationship{}
|> changeset(%{
relationship_type: relationship_type,
source_id: source.id,
target_id: target.id,
- expires_at: expires_at
+ expires_at: expires_at,
+ inserted_at: datetime_impl().utc_now()
})
|> Repo.insert(
on_conflict: {:replace_all_except, [:id, :inserted_at]},
conflict_target: [:source_id, :relationship_type, :target_id],
returning: true
)
end
def delete(relationship_type, %User{} = source, %User{} = target) do
attrs = %{relationship_type: relationship_type, source_id: source.id, target_id: target.id}
case Repo.get_by(UserRelationship, attrs) do
%UserRelationship{} = existing_record -> Repo.delete(existing_record)
nil -> {:ok, nil}
end
end
def dictionary(
source_users,
target_users,
source_to_target_rel_types \\ nil,
target_to_source_rel_types \\ nil
)
def dictionary(
_source_users,
_target_users,
[] = _source_to_target_rel_types,
[] = _target_to_source_rel_types
) do
[]
end
def dictionary(
source_users,
target_users,
source_to_target_rel_types,
target_to_source_rel_types
)
when is_list(source_users) and is_list(target_users) do
source_user_ids = User.binary_id(source_users)
target_user_ids = User.binary_id(target_users)
get_rel_type_codes = fn rel_type -> user_relationship_mappings()[rel_type] end
source_to_target_rel_types =
Enum.map(source_to_target_rel_types || user_relationship_types(), &get_rel_type_codes.(&1))
target_to_source_rel_types =
Enum.map(target_to_source_rel_types || user_relationship_types(), &get_rel_type_codes.(&1))
__MODULE__
|> where(
fragment(
"(source_id = ANY(?) AND target_id = ANY(?) AND relationship_type = ANY(?)) OR \
(source_id = ANY(?) AND target_id = ANY(?) AND relationship_type = ANY(?))",
^source_user_ids,
^target_user_ids,
^source_to_target_rel_types,
^target_user_ids,
^source_user_ids,
^target_to_source_rel_types
)
)
|> select([ur], [ur.relationship_type, ur.source_id, ur.target_id])
|> Repo.all()
end
def exists?(dictionary, rel_type, source, target, func) do
cond do
is_nil(source) or is_nil(target) ->
false
dictionary ->
[rel_type, source.id, target.id] in dictionary
true ->
func.(source, target)
end
end
@doc ":relationships option for StatusView / AccountView / NotificationView"
def view_relationships_option(reading_user, actors, opts \\ [])
def view_relationships_option(nil = _reading_user, _actors, _opts) do
%{user_relationships: [], following_relationships: []}
end
def view_relationships_option(%User{} = reading_user, actors, opts) do
{source_to_target_rel_types, target_to_source_rel_types} =
case opts[:subset] do
:source_mutes ->
# Used for statuses rendering (FE needs `muted` flag for each status when statuses load)
{[:mute], []}
nil ->
{[:block, :mute, :notification_mute, :reblog_mute], [:block, :inverse_subscription]}
unknown ->
raise "Unsupported :subset option value: #{inspect(unknown)}"
end
user_relationships =
UserRelationship.dictionary(
[reading_user],
actors,
source_to_target_rel_types,
target_to_source_rel_types
)
following_relationships =
case opts[:subset] do
:source_mutes ->
[]
nil ->
FollowingRelationship.all_between_user_sets([reading_user], actors)
unknown ->
raise "Unsupported :subset option value: #{inspect(unknown)}"
end
%{user_relationships: user_relationships, following_relationships: following_relationships}
end
defp validate_not_self_relationship(%Changeset{} = changeset) do
changeset
|> validate_source_id_target_id_inequality()
|> validate_target_id_source_id_inequality()
end
defp validate_source_id_target_id_inequality(%Changeset{} = changeset) do
validate_change(changeset, :source_id, fn _, source_id ->
if source_id == get_field(changeset, :target_id) do
[source_id: "can't be equal to target_id"]
else
[]
end
end)
end
defp validate_target_id_source_id_inequality(%Changeset{} = changeset) do
validate_change(changeset, :target_id, fn _, target_id ->
if target_id == get_field(changeset, :source_id) do
[target_id: "can't be equal to source_id"]
else
[]
end
end)
end
end
diff --git a/test/pleroma/user_relationship_test.exs b/test/pleroma/user_relationship_test.exs
index 7d205a746..5b43cb2b6 100644
--- a/test/pleroma/user_relationship_test.exs
+++ b/test/pleroma/user_relationship_test.exs
@@ -1,136 +1,142 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.UserRelationshipTest do
+ alias Pleroma.DateTimeMock
alias Pleroma.UserRelationship
- use Pleroma.DataCase, async: false
+ use Pleroma.DataCase, async: true
- import Mock
+ import Mox
import Pleroma.Factory
describe "*_exists?/2" do
setup do
{:ok, users: insert_list(2, :user)}
end
test "returns false if record doesn't exist", %{users: [user1, user2]} do
refute UserRelationship.block_exists?(user1, user2)
refute UserRelationship.mute_exists?(user1, user2)
refute UserRelationship.notification_mute_exists?(user1, user2)
refute UserRelationship.reblog_mute_exists?(user1, user2)
refute UserRelationship.inverse_subscription_exists?(user1, user2)
end
test "returns true if record exists", %{users: [user1, user2]} do
for relationship_type <- [
:block,
:mute,
:notification_mute,
:reblog_mute,
:inverse_subscription
] do
insert(:user_relationship,
source: user1,
target: user2,
relationship_type: relationship_type
)
end
assert UserRelationship.block_exists?(user1, user2)
assert UserRelationship.mute_exists?(user1, user2)
assert UserRelationship.notification_mute_exists?(user1, user2)
assert UserRelationship.reblog_mute_exists?(user1, user2)
assert UserRelationship.inverse_subscription_exists?(user1, user2)
end
end
describe "create_*/2" do
setup do
{:ok, users: insert_list(2, :user)}
end
test "creates user relationship record if it doesn't exist", %{users: [user1, user2]} do
+ DateTimeMock
+ |> stub_with(Pleroma.DateTime.Impl)
+
for relationship_type <- [
:block,
:mute,
:notification_mute,
:reblog_mute,
:inverse_subscription
] do
insert(:user_relationship,
source: user1,
target: user2,
relationship_type: relationship_type
)
end
UserRelationship.create_block(user1, user2)
UserRelationship.create_mute(user1, user2)
UserRelationship.create_notification_mute(user1, user2)
UserRelationship.create_reblog_mute(user1, user2)
UserRelationship.create_inverse_subscription(user1, user2)
assert UserRelationship.block_exists?(user1, user2)
assert UserRelationship.mute_exists?(user1, user2)
assert UserRelationship.notification_mute_exists?(user1, user2)
assert UserRelationship.reblog_mute_exists?(user1, user2)
assert UserRelationship.inverse_subscription_exists?(user1, user2)
end
test "if record already exists, returns it", %{users: [user1, user2]} do
- user_block =
- with_mock NaiveDateTime, [:passthrough], utc_now: fn -> ~N[2017-03-17 17:09:58] end do
- {:ok, %{inserted_at: ~N[2017-03-17 17:09:58]}} =
- UserRelationship.create_block(user1, user2)
- end
+ fixed_datetime = ~N[2017-03-17 17:09:58]
+
+ Pleroma.DateTimeMock
+ |> expect(:utc_now, 2, fn -> fixed_datetime end)
+
+ {:ok, %{inserted_at: ^fixed_datetime}} = UserRelationship.create_block(user1, user2)
- assert user_block == UserRelationship.create_block(user1, user2)
+ # Test the idempotency without caring about the exact time
+ assert {:ok, _} = UserRelationship.create_block(user1, user2)
end
end
describe "delete_*/2" do
setup do
{:ok, users: insert_list(2, :user)}
end
test "deletes user relationship record if it exists", %{users: [user1, user2]} do
for relationship_type <- [
:block,
:mute,
:notification_mute,
:reblog_mute,
:inverse_subscription
] do
insert(:user_relationship,
source: user1,
target: user2,
relationship_type: relationship_type
)
end
assert {:ok, %UserRelationship{}} = UserRelationship.delete_block(user1, user2)
assert {:ok, %UserRelationship{}} = UserRelationship.delete_mute(user1, user2)
assert {:ok, %UserRelationship{}} = UserRelationship.delete_notification_mute(user1, user2)
assert {:ok, %UserRelationship{}} = UserRelationship.delete_reblog_mute(user1, user2)
assert {:ok, %UserRelationship{}} =
UserRelationship.delete_inverse_subscription(user1, user2)
refute UserRelationship.block_exists?(user1, user2)
refute UserRelationship.mute_exists?(user1, user2)
refute UserRelationship.notification_mute_exists?(user1, user2)
refute UserRelationship.reblog_mute_exists?(user1, user2)
refute UserRelationship.inverse_subscription_exists?(user1, user2)
end
test "if record does not exist, returns {:ok, nil}", %{users: [user1, user2]} do
assert {:ok, nil} = UserRelationship.delete_block(user1, user2)
assert {:ok, nil} = UserRelationship.delete_mute(user1, user2)
assert {:ok, nil} = UserRelationship.delete_notification_mute(user1, user2)
assert {:ok, nil} = UserRelationship.delete_reblog_mute(user1, user2)
assert {:ok, nil} = UserRelationship.delete_inverse_subscription(user1, user2)
end
end
end
diff --git a/test/support/mocks.ex b/test/support/mocks.ex
index d84958e15..228b9882e 100644
--- a/test/support/mocks.ex
+++ b/test/support/mocks.ex
@@ -1,35 +1,37 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
Mox.defmock(Pleroma.CachexMock, for: Pleroma.Caching)
Mox.defmock(Pleroma.Web.ActivityPub.ObjectValidatorMock,
for: Pleroma.Web.ActivityPub.ObjectValidator.Validating
)
Mox.defmock(Pleroma.Web.ActivityPub.MRFMock,
for: Pleroma.Web.ActivityPub.MRF.PipelineFiltering
)
Mox.defmock(Pleroma.Web.ActivityPub.ActivityPubMock,
for: [
Pleroma.Web.ActivityPub.ActivityPub.Persisting,
Pleroma.Web.ActivityPub.ActivityPub.Streaming
]
)
Mox.defmock(Pleroma.Web.ActivityPub.SideEffectsMock,
for: Pleroma.Web.ActivityPub.SideEffects.Handling
)
Mox.defmock(Pleroma.Web.FederatorMock, for: Pleroma.Web.Federator.Publishing)
Mox.defmock(Pleroma.ConfigMock, for: Pleroma.Config.Getting)
Mox.defmock(Pleroma.UnstubbedConfigMock, for: Pleroma.Config.Getting)
Mox.defmock(Pleroma.StaticStubbedConfigMock, for: Pleroma.Config.Getting)
Mox.defmock(Pleroma.StubbedHTTPSignaturesMock, for: Pleroma.HTTPSignaturesAPI)
Mox.defmock(Pleroma.LoggerMock, for: Pleroma.Logging)
Mox.defmock(Pleroma.Uploaders.S3.ExAwsMock, for: Pleroma.Uploaders.S3.ExAwsAPI)
+
+Mox.defmock(Pleroma.DateTimeMock, for: Pleroma.DateTime)

File Metadata

Mime Type
text/x-diff
Expires
Tue, Oct 13, 6:49 PM (1 d, 21 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1786867
Default Alt Text
(22 KB)

Event Timeline