9.4 KiB · text History 6280797
defmodule GitGudWeb.ModerationPageTest do
@moduledoc """
The org and repo moderation pages: who can reach them, what they
show, and that a moderator can only act within their own scope.
"""
use GitGudWeb.ConnCase, async: false
import Phoenix.LiveViewTest
import GitGud.AccountsFixtures
import GitGud.ForgeFixtures
alias GitGud.Issues
alias GitGud.Moderation
alias GitGud.Organizations
alias GitGud.Reports
alias GitGud.Repositories
defp repo_mod_path(repo) do
handle = Repositories.Storage.repo_handle(GitGud.Repo.preload(repo, [:owner, :organization]))
~p"/r/#{handle}/#{repo.name}/settings/moderation"
end
defp org_setup(handle) do
admin = user_fixture()
{:ok, org} = Organizations.create_organization(admin, %{"handle" => handle})
{:ok, repo} =
Repositories.create_repository_for_org(org, admin, %{
"name" => "modrepo",
"visibility" => "public"
})
{admin, org, repo}
end
describe "access" do
test "a repo owner reaches their moderation page", %{conn: conn} do
{owner, repo} = repository_fixture()
{:ok, _lv, html} = live(log_in_user(conn, owner), repo_mod_path(repo))
assert html =~ "Moderation"
end
test "an unrelated user is bounced", %{conn: conn} do
{_owner, repo} = repository_fixture(%{visibility: "public"})
outsider = user_fixture()
assert {:error, {:live_redirect, _}} =
live(log_in_user(conn, outsider), repo_mod_path(repo))
end
test "an org admin reaches the org page", %{conn: conn} do
{admin, org, _repo} = org_setup("mod-access")
{:ok, _lv, html} =
live(log_in_user(conn, admin), ~p"/orgs/#{org.handle}/settings/moderation")
assert html =~ "Moderation"
assert html =~ "every repository"
end
test "a plain org member is bounced from the org page", %{conn: conn} do
{_admin, org, _repo} = org_setup("mod-member")
member = user_fixture()
{:ok, _} = Organizations.add_member(org, member, "member")
assert {:error, {:live_redirect, _}} =
live(log_in_user(conn, member), ~p"/orgs/#{org.handle}/settings/moderation")
end
end
describe "the report queue" do
test "shows a report about this repo's content", %{conn: conn} do
{owner, repo} = repository_fixture(%{visibility: "public"})
author = user_fixture()
issue = issue_fixture(repo, author, %{"title" => "Reported thing"})
reporter = user_fixture()
{:ok, _} = Reports.open(reporter, {"issue", issue.id}, "spam")
{:ok, _lv, html} = live(log_in_user(conn, owner), repo_mod_path(repo))
assert html =~ "spam"
assert html =~ "Reported thing"
assert html =~ reporter.handle
end
test "a report about another repo doesn't appear", %{conn: conn} do
{owner, repo} = repository_fixture(%{visibility: "public"})
{other_owner, other} = repository_fixture(%{visibility: "public"})
issue = issue_fixture(other, other_owner, %{"title" => "Elsewhere"})
{:ok, _} = Reports.open(user_fixture(), {"issue", issue.id}, "spam")
{:ok, _lv, html} = live(log_in_user(conn, owner), repo_mod_path(repo))
refute html =~ "Elsewhere"
assert html =~ "Nothing reported here."
end
test "the org page sees reports from its repos", %{conn: conn} do
{admin, org, repo} = org_setup("mod-org-queue")
issue = issue_fixture(repo, user_fixture(), %{"title" => "Org-owned issue"})
{:ok, _} = Reports.open(user_fixture(), {"issue", issue.id}, "abuse")
{:ok, _lv, html} =
live(log_in_user(conn, admin), ~p"/orgs/#{org.handle}/settings/moderation")
assert html =~ "Org-owned issue"
end
test "dismissing closes it", %{conn: conn} do
{owner, repo} = repository_fixture(%{visibility: "public"})
issue = issue_fixture(repo, user_fixture())
{:ok, report} = Reports.open(user_fixture(), {"issue", issue.id}, "spam")
{:ok, lv, _html} = live(log_in_user(conn, owner), repo_mod_path(repo))
html = render_hook(lv, "dismiss_report", %{"id" => to_string(report.id)})
assert html =~ "Nothing reported here."
assert Reports.get_report!(report.id).state == "dismissed"
end
test "replacing content keeps the original for admins", %{conn: conn} do
{owner, repo} = repository_fixture(%{visibility: "public"})
issue = issue_fixture(repo, user_fixture(), %{"body" => "the offending words"})
{:ok, report} = Reports.open(user_fixture(), {"issue", issue.id}, "abuse")
{:ok, lv, _html} = live(log_in_user(conn, owner), repo_mod_path(repo))
render_hook(lv, "moderate_report", %{"id" => to_string(report.id)})
reloaded = Issues.get_issue!(repo, issue.number)
assert reloaded.moderated_at
assert reloaded.original_body == "the offending words"
assert Reports.get_report!(report.id).state == "actioned"
end
test "a moderator can't act on a report outside their scope", %{conn: conn} do
{owner, repo} = repository_fixture(%{visibility: "public"})
{other_owner, other} = repository_fixture(%{visibility: "public"})
issue = issue_fixture(other, other_owner)
{:ok, foreign} = Reports.open(user_fixture(), {"issue", issue.id}, "spam")
{:ok, lv, _html} = live(log_in_user(conn, owner), repo_mod_path(repo))
html = render_hook(lv, "dismiss_report", %{"id" => to_string(foreign.id)})
assert html =~ "isn't yours to act on"
assert Reports.get_report!(foreign.id).state == "open"
end
end
describe "bans" do
test "banning by handle stops them writing", %{conn: conn} do
{owner, repo} = repository_fixture(%{visibility: "public"})
nuisance = user_fixture()
{:ok, lv, _html} = live(log_in_user(conn, owner), repo_mod_path(repo))
html =
lv
|> form("form[phx-submit=ban_user]", %{handle: nuisance.handle, reason: "spam"})
|> render_submit()
assert html =~ nuisance.handle
assert Moderation.banned?(repo, nuisance)
assert {:error, :banned} = Issues.create_issue(repo, nuisance, %{"title" => "nope"})
end
test "an unknown handle is reported", %{conn: conn} do
{owner, repo} = repository_fixture()
{:ok, lv, _html} = live(log_in_user(conn, owner), repo_mod_path(repo))
html = lv |> form("form[phx-submit=ban_user]", %{handle: "nobody"}) |> render_submit()
assert html =~ "No such user."
end
test "a moderator can't ban themselves", %{conn: conn} do
{owner, repo} = repository_fixture()
{:ok, lv, _html} = live(log_in_user(conn, owner), repo_mod_path(repo))
html = lv |> form("form[phx-submit=ban_user]", %{handle: owner.handle}) |> render_submit()
assert html =~ "can't ban yourself"
refute Moderation.banned?(repo, owner)
end
test "lifting restores them", %{conn: conn} do
{owner, repo} = repository_fixture(%{visibility: "public"})
nuisance = user_fixture()
{:ok, ban} = Moderation.ban(repo, nuisance, banned_by: owner)
{:ok, lv, _html} = live(log_in_user(conn, owner), repo_mod_path(repo))
render_hook(lv, "lift_ban", %{"id" => to_string(ban.id)})
refute Moderation.banned?(repo, nuisance)
assert {:ok, _} = Issues.create_issue(repo, nuisance, %{"title" => "back"})
end
test "a repo admin can't lift the owning org's ban", %{conn: conn} do
{admin, org, repo} = org_setup("mod-no-undercut")
nuisance = user_fixture()
{:ok, org_ban} = Moderation.ban(org, nuisance, banned_by: admin)
{:ok, lv, _html} = live(log_in_user(conn, admin), repo_mod_path(repo))
html = render_hook(lv, "lift_ban", %{"id" => to_string(org_ban.id)})
assert html =~ "isn't yours to lift"
assert Moderation.banned?(repo, nuisance)
end
test "banning the reported author works from the queue", %{conn: conn} do
{owner, repo} = repository_fixture(%{visibility: "public"})
author = user_fixture()
issue = issue_fixture(repo, author)
{:ok, report} = Reports.open(user_fixture(), {"issue", issue.id}, "abuse")
{:ok, lv, _html} = live(log_in_user(conn, owner), repo_mod_path(repo))
render_hook(lv, "ban_reported_author", %{"id" => to_string(report.id)})
assert Moderation.banned?(repo, author)
end
test "a dated ban is recorded with its expiry", %{conn: conn} do
{owner, repo} = repository_fixture(%{visibility: "public"})
nuisance = user_fixture()
tomorrow = Date.utc_today() |> Date.add(1) |> Date.to_iso8601()
{:ok, lv, _html} = live(log_in_user(conn, owner), repo_mod_path(repo))
_ =
lv
|> form("form[phx-submit=ban_user]", %{handle: nuisance.handle, expires_on: tomorrow})
|> render_submit()
assert [ban] = Moderation.list_bans(repo)
assert ban.expires_at
assert Moderation.banned?(repo, nuisance)
end
test "an org ban shows on the org page, not the repo's list", %{conn: conn} do
{admin, org, repo} = org_setup("mod-listing")
nuisance = user_fixture()
{:ok, _} = Moderation.ban(org, nuisance, banned_by: admin)
{:ok, _lv, org_html} =
live(log_in_user(conn, admin), ~p"/orgs/#{org.handle}/settings/moderation")
{:ok, _lv, repo_html} = live(log_in_user(conn, admin), repo_mod_path(repo))
assert org_html =~ nuisance.handle
assert repo_html =~ "Nobody is banned from this repository"
# But the repo page says where to look.
assert repo_html =~ "its moderation page"
end
end
end