Port of Akkoma PR 1014 with a few changes: - comments regarding akkomafe changed to Pleroma-FE when applicable - different error message for replying to/interacting with invisible post in Pleroma.Web.CommonAPI.ActivityDraft.in_reply_to/1 - split "doesn't do funny things to other users favs" test into three: - can't unfavourite post that isn't favourited - can't unfavourite other user's favs - can't unfavourite other user's favs using their activity - switched order of args for some CommonAPI function since Akkoma hasn't backported our old change for that Pleroma.Web.CommonAPI.ActivityDraft.in_reply_to/1 now refactored to use `with` statement as in Akkoma. Some defp in_reply_to/1 were therefore removed Original PR author: Oneric Original commit message: It doesn't make sense to like, react, reply, etc to something you cannot see and is unexpected for the author of the interacted with post and might make them believe the reacting user actually _can_ see the post. Wrt to fav, reblog, reaction indexes the missing visibility check was also leaking some (presumably/hopefully) low-severity data. Add full-API test for all modes of interactions with private posts.
374 lines
13 KiB
Elixir
374 lines
13 KiB
Elixir
# Pleroma: A lightweight social networking server
|
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
|
# SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
defmodule Pleroma.Conversation.ParticipationTest do
|
|
use Pleroma.DataCase, async: true
|
|
import Pleroma.Factory
|
|
alias Pleroma.Conversation
|
|
alias Pleroma.Conversation.Participation
|
|
alias Pleroma.Repo
|
|
alias Pleroma.User
|
|
alias Pleroma.Web.CommonAPI
|
|
|
|
test "getting a participation will also preload things" do
|
|
user = insert(:user)
|
|
other_user = insert(:user)
|
|
|
|
{:ok, _activity} =
|
|
CommonAPI.post(user, %{status: "Hey @#{other_user.nickname}.", visibility: "direct"})
|
|
|
|
[participation] = Participation.for_user(user)
|
|
|
|
participation = Participation.get(participation.id, preload: [:conversation])
|
|
|
|
assert %Pleroma.Conversation{} = participation.conversation
|
|
end
|
|
|
|
test "for a new conversation or a reply, it doesn't mark the author's participation as unread" do
|
|
user = insert(:user)
|
|
other_user = insert(:user)
|
|
|
|
{:ok, _} =
|
|
CommonAPI.post(user, %{status: "Hey @#{other_user.nickname}.", visibility: "direct"})
|
|
|
|
user = User.get_cached_by_id(user.id)
|
|
other_user = User.get_cached_by_id(other_user.id)
|
|
|
|
[%{read: true}] = Participation.for_user(user)
|
|
[%{read: false} = participation] = Participation.for_user(other_user)
|
|
assert Participation.unread_count(user) == 0
|
|
assert Participation.unread_count(other_user) == 1
|
|
|
|
{:ok, _} =
|
|
CommonAPI.post(other_user, %{
|
|
status: "Hey @#{user.nickname}.",
|
|
visibility: "direct",
|
|
in_reply_to_conversation_id: participation.id
|
|
})
|
|
|
|
user = User.get_cached_by_id(user.id)
|
|
other_user = User.get_cached_by_id(other_user.id)
|
|
|
|
[%{read: false}] = Participation.for_user(user)
|
|
[%{read: true}] = Participation.for_user(other_user)
|
|
|
|
assert Participation.unread_count(user) == 1
|
|
assert Participation.unread_count(other_user) == 0
|
|
end
|
|
|
|
test "for a new conversation, it sets the recipients of the participation" do
|
|
user = insert(:user)
|
|
other_user = insert(:user)
|
|
third_user = insert(:user)
|
|
|
|
{:ok, activity} =
|
|
CommonAPI.post(user, %{status: "Hey @#{other_user.nickname}.", visibility: "direct"})
|
|
|
|
user = User.get_cached_by_id(user.id)
|
|
other_user = User.get_cached_by_id(other_user.id)
|
|
[participation] = Participation.for_user(user)
|
|
participation = Pleroma.Repo.preload(participation, :recipients)
|
|
|
|
assert length(participation.recipients) == 2
|
|
assert user in participation.recipients
|
|
assert other_user in participation.recipients
|
|
|
|
# Mentioning another user in the same conversation will not add a new recipients.
|
|
|
|
{:ok, _activity} =
|
|
CommonAPI.post(user, %{
|
|
in_reply_to_status_id: activity.id,
|
|
status: "Hey @#{third_user.nickname}.",
|
|
visibility: "direct"
|
|
})
|
|
|
|
[participation] = Participation.for_user(user)
|
|
participation = Pleroma.Repo.preload(participation, :recipients)
|
|
|
|
assert length(participation.recipients) == 2
|
|
end
|
|
|
|
test "it creates a participation for a conversation and a user" do
|
|
user = insert(:user)
|
|
conversation = insert(:conversation)
|
|
|
|
{:ok, %Participation{} = participation} =
|
|
Participation.create_for_user_and_conversation(user, conversation)
|
|
|
|
{:ok, participation} = time_travel(participation, -2)
|
|
|
|
assert participation.user_id == user.id
|
|
assert participation.conversation_id == conversation.id
|
|
|
|
# Creating again returns the same participation
|
|
{:ok, %Participation{} = participation_two} =
|
|
Participation.create_for_user_and_conversation(user, conversation)
|
|
|
|
assert participation.id == participation_two.id
|
|
refute participation.updated_at == participation_two.updated_at
|
|
end
|
|
|
|
test "recreating an existing participations sets it to unread" do
|
|
participation = insert(:participation, %{read: true})
|
|
|
|
{:ok, participation} =
|
|
Participation.create_for_user_and_conversation(
|
|
participation.user,
|
|
participation.conversation
|
|
)
|
|
|
|
refute participation.read
|
|
end
|
|
|
|
test "it marks a participation as read" do
|
|
participation = insert(:participation, %{updated_at: ~N[2017-07-17 17:09:58], read: false})
|
|
{:ok, updated_participation} = Participation.mark_as_read(participation)
|
|
|
|
assert updated_participation.read
|
|
assert :gt = NaiveDateTime.compare(updated_participation.updated_at, participation.updated_at)
|
|
end
|
|
|
|
test "it marks a participation as unread" do
|
|
participation = insert(:participation, %{read: true})
|
|
{:ok, participation} = Participation.mark_as_unread(participation)
|
|
|
|
refute participation.read
|
|
end
|
|
|
|
test "it marks all the user's participations as read" do
|
|
user = insert(:user)
|
|
other_user = insert(:user)
|
|
participation1 = insert(:participation, %{read: false, user: user})
|
|
participation2 = insert(:participation, %{read: false, user: user})
|
|
participation3 = insert(:participation, %{read: false, user: other_user})
|
|
|
|
{:ok, _, [%{read: true}, %{read: true}]} = Participation.mark_all_as_read(user)
|
|
|
|
assert Participation.get(participation1.id).read == true
|
|
assert Participation.get(participation2.id).read == true
|
|
assert Participation.get(participation3.id).read == false
|
|
end
|
|
|
|
test "gets all the participations for a user, ordered by updated at descending" do
|
|
user = insert(:user)
|
|
{:ok, activity_one} = CommonAPI.post(user, %{status: "x", visibility: "direct"})
|
|
{:ok, activity_two} = CommonAPI.post(user, %{status: "x", visibility: "direct"})
|
|
|
|
{:ok, activity_three} =
|
|
CommonAPI.post(user, %{
|
|
status: "x",
|
|
visibility: "direct",
|
|
in_reply_to_status_id: activity_one.id
|
|
})
|
|
|
|
# Offset participations because the accuracy of updated_at is down to a second
|
|
|
|
for {activity, offset} <- [{activity_two, 1}, {activity_three, 2}] do
|
|
conversation = Conversation.get_for_ap_id(activity.data["context"])
|
|
participation = Participation.for_user_and_conversation(user, conversation)
|
|
updated_at = NaiveDateTime.add(Map.get(participation, :updated_at), offset)
|
|
|
|
Ecto.Changeset.change(participation, %{updated_at: updated_at})
|
|
|> Repo.update!()
|
|
end
|
|
|
|
assert [participation_one, participation_two] = Participation.for_user(user)
|
|
|
|
object2 = Pleroma.Object.normalize(activity_two, fetch: false)
|
|
object3 = Pleroma.Object.normalize(activity_three, fetch: false)
|
|
|
|
user = Repo.get(Pleroma.User, user.id)
|
|
|
|
assert participation_one.conversation.ap_id == object3.data["context"]
|
|
assert participation_two.conversation.ap_id == object2.data["context"]
|
|
assert participation_one.conversation.users == [user]
|
|
|
|
# Pagination
|
|
assert [participation_one] = Participation.for_user(user, %{"limit" => 1})
|
|
|
|
assert participation_one.conversation.ap_id == object3.data["context"]
|
|
|
|
# With last_activity_id
|
|
assert [participation_one] =
|
|
Participation.for_user_with_last_activity_id(user, %{"limit" => 1})
|
|
|
|
assert participation_one.last_activity_id == activity_three.id
|
|
end
|
|
|
|
test "Doesn't die when the conversation gets empty" do
|
|
user = insert(:user)
|
|
|
|
{:ok, activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"})
|
|
[participation] = Participation.for_user_with_last_activity_id(user)
|
|
|
|
assert participation.last_activity_id == activity.id
|
|
|
|
{:ok, _} = CommonAPI.delete(activity.id, user)
|
|
|
|
[] = Participation.for_user_with_last_activity_id(user)
|
|
end
|
|
|
|
test "it sets recipients, always keeping the owner of the participation even when not explicitly set" do
|
|
user = insert(:user)
|
|
other_user = insert(:user)
|
|
|
|
{:ok, _activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"})
|
|
[participation] = Participation.for_user_with_last_activity_id(user)
|
|
|
|
participation = Repo.preload(participation, :recipients)
|
|
user = User.get_cached_by_id(user.id)
|
|
|
|
assert participation.recipients |> length() == 1
|
|
assert user in participation.recipients
|
|
|
|
{:ok, participation} = Participation.set_recipients(participation, [other_user.id])
|
|
|
|
assert participation.recipients |> length() == 2
|
|
assert user in participation.recipients
|
|
assert other_user in participation.recipients
|
|
end
|
|
|
|
describe "blocking" do
|
|
test "when the user blocks a recipient, the existing conversations with them are marked as read" do
|
|
blocker = insert(:user)
|
|
blocked = insert(:user)
|
|
third_user = insert(:user)
|
|
|
|
{:ok, _direct1} =
|
|
CommonAPI.post(third_user, %{
|
|
status: "Hi @#{blocker.nickname}",
|
|
visibility: "direct"
|
|
})
|
|
|
|
{:ok, _direct2} =
|
|
CommonAPI.post(third_user, %{
|
|
status: "Hi @#{blocker.nickname}, @#{blocked.nickname}",
|
|
visibility: "direct"
|
|
})
|
|
|
|
{:ok, _direct3} =
|
|
CommonAPI.post(blocked, %{
|
|
status: "Hi @#{blocker.nickname}",
|
|
visibility: "direct"
|
|
})
|
|
|
|
{:ok, _direct4} =
|
|
CommonAPI.post(blocked, %{
|
|
status: "Hi @#{blocker.nickname}, @#{third_user.nickname}",
|
|
visibility: "direct"
|
|
})
|
|
|
|
assert [%{read: false}, %{read: false}, %{read: false}, %{read: false}] =
|
|
Participation.for_user(blocker)
|
|
|
|
assert Participation.unread_count(blocker) == 4
|
|
|
|
{:ok, _user_relationship} = User.block(blocker, blocked)
|
|
|
|
# The conversations with the blocked user are marked as read
|
|
assert [%{read: true}, %{read: true}, %{read: true}, %{read: false}] =
|
|
Participation.for_user(blocker)
|
|
|
|
assert Participation.unread_count(blocker) == 1
|
|
|
|
# The conversation is not marked as read for the blocked user
|
|
assert [_, _, %{read: false}] = Participation.for_user(blocked)
|
|
assert Participation.unread_count(blocker) == 1
|
|
|
|
# The conversation is not marked as read for the third user
|
|
assert [%{read: false}, _, _] = Participation.for_user(third_user)
|
|
assert Participation.unread_count(third_user) == 1
|
|
end
|
|
|
|
test "the new conversation with the blocked user is not marked as unread " do
|
|
blocker = insert(:user)
|
|
blocked = insert(:user)
|
|
third_user = insert(:user)
|
|
|
|
{:ok, _user_relationship} = User.block(blocker, blocked)
|
|
|
|
# When the blocked user is the author
|
|
{:ok, _direct1} =
|
|
CommonAPI.post(blocked, %{
|
|
status: "Hi @#{blocker.nickname}",
|
|
visibility: "direct"
|
|
})
|
|
|
|
assert [%{read: true}] = Participation.for_user(blocker)
|
|
assert Participation.unread_count(blocker) == 0
|
|
|
|
# When the blocked user is a recipient
|
|
{:ok, _direct2} =
|
|
CommonAPI.post(third_user, %{
|
|
status: "Hi @#{blocker.nickname}, @#{blocked.nickname}",
|
|
visibility: "direct"
|
|
})
|
|
|
|
assert [%{read: true}, %{read: true}] = Participation.for_user(blocker)
|
|
assert Participation.unread_count(blocker) == 0
|
|
|
|
assert [%{read: false}, _] = Participation.for_user(blocked)
|
|
assert Participation.unread_count(blocked) == 1
|
|
end
|
|
|
|
test "the conversation with the blocked user is not marked as unread on a reply" do
|
|
blocker = insert(:user)
|
|
blocked = insert(:user)
|
|
third_user = insert(:user)
|
|
|
|
{:ok, _direct1} =
|
|
CommonAPI.post(blocker, %{
|
|
status: "Hi @#{third_user.nickname}, @#{blocked.nickname}",
|
|
visibility: "direct"
|
|
})
|
|
|
|
{:ok, _user_relationship} = User.block(blocker, blocked)
|
|
assert [%{read: true}] = Participation.for_user(blocker)
|
|
|
|
assert Participation.unread_count(blocker) == 0
|
|
assert [blocked_participation] = Participation.for_user(blocked)
|
|
|
|
# When it's a reply from the blocked user
|
|
{:ok, _direct2} =
|
|
CommonAPI.post(blocked, %{
|
|
status: "@#{third_user.nickname}, #{blocker.nickname} reply",
|
|
visibility: "direct",
|
|
in_reply_to_conversation_id: blocked_participation.id
|
|
})
|
|
|
|
assert [%{read: true}] = Participation.for_user(blocker)
|
|
|
|
assert Participation.unread_count(blocker) == 0
|
|
assert [third_user_participation] = Participation.for_user(third_user)
|
|
|
|
# When it's a reply from the third user
|
|
{:ok, _direct3} =
|
|
CommonAPI.post(third_user, %{
|
|
status: "reply",
|
|
visibility: "direct",
|
|
in_reply_to_conversation_id: third_user_participation.id
|
|
})
|
|
|
|
assert [%{read: true}] = Participation.for_user(blocker)
|
|
assert Participation.unread_count(blocker) == 0
|
|
|
|
# Marked as unread for the blocked user
|
|
assert [%{read: false}] = Participation.for_user(blocked)
|
|
|
|
assert Participation.unread_count(blocked) == 1
|
|
end
|
|
end
|
|
|
|
test "deletes a conversation" do
|
|
user = insert(:user)
|
|
other_user = insert(:user)
|
|
|
|
{:ok, _activity} =
|
|
CommonAPI.post(user, %{status: "Hey @#{other_user.nickname}.", visibility: "direct"})
|
|
|
|
assert [participation] = Participation.for_user(other_user)
|
|
assert {:ok, _} = Participation.delete(participation)
|
|
assert [] == Participation.for_user(other_user)
|
|
end
|
|
end
|