analytics/test/plausible_web/controllers/invitation_controller_test.exs
Adrian Gruntkowski 9d97dc1912
Move limit enforcement to accepting site ownership transfer (#3612)
* Move limit enforcement to accepting site ownerhsip transfer

* enforce pageview limit on ownership transfer accept

* Refactor plan limit check logic

* Extract `ensure_can_take_ownership` to `Invitations` context and refactor

* Improve styling of exceeded limits notice in invitation dialog and disable button

* styling improvements to notice

* make transfer_ownership return transfer to self error

* do not allow transferring to user without active subscription WIP

* Add missing typespec and improve existing ones

* Fix formatting

* Explicitly label direct match on function argument for clarity

* Slightly refactor `CreateInvitation.bulk_transfer_ownership_direct`

* Exclude quota enforcement tests from small build test suite

* Remove unused return type from `invite_error()` union type

* Do not block plan upgrade when there's pending ownership transfer

* Don't block and only warn about missing features on transfer

* Remove `x-init` attribute used for debugging

* Add tests for `Quota.monthly_pageview_usage/2`

* Test and improve site admin ownership transfer actions

* Extend tests for `AcceptInvitation.transfer_ownership`

* Test transfer ownership controller level accept action error cases

* Test choosing plan by user without sites but with a pending ownership transfer

* Test invitation x-data in sites LV

* Remove sitelocker trigger in invitation acceptance code and simplify logic

* Add Quota test for `user.allow_next_upgrade_override` being set

* ignore pageview limit only when subscribing to plan

* Use sandbox Paddle instance for staging

* Use sandbox paddle key for staging and dev

---------

Co-authored-by: Robert Joonas <robertjoonas16@gmail.com>
2023-12-20 14:56:49 +00:00

246 lines
7.6 KiB
Elixir

defmodule PlausibleWeb.Site.InvitationControllerTest do
use PlausibleWeb.ConnCase, async: true
use Plausible.Repo
use Bamboo.Test
setup [:create_user, :log_in]
describe "POST /sites/invitations/:invitation_id/accept" do
test "converts the invitation into a membership", %{conn: conn, user: user} do
site = insert(:site)
invitation =
insert(:invitation,
site_id: site.id,
inviter: build(:user),
email: user.email,
role: :admin
)
conn = post(conn, "/sites/invitations/#{invitation.invitation_id}/accept")
assert Phoenix.Flash.get(conn.assigns.flash, :success) ==
"You now have access to #{site.domain}"
assert redirected_to(conn) == "/#{URI.encode_www_form(site.domain)}"
refute Repo.exists?(from(i in Plausible.Auth.Invitation, where: i.email == ^user.email))
membership = Repo.get_by(Plausible.Site.Membership, user_id: user.id, site_id: site.id)
assert membership.role == :admin
end
test "does not crash if clicked for the 2nd time in another tab", %{conn: conn, user: user} do
site = insert(:site)
invitation =
insert(:invitation,
site_id: site.id,
inviter: build(:user),
email: user.email,
role: :admin
)
c1 = post(conn, "/sites/invitations/#{invitation.invitation_id}/accept")
assert redirected_to(c1) == "/#{URI.encode_www_form(site.domain)}"
assert Phoenix.Flash.get(c1.assigns.flash, :success) ==
"You now have access to #{site.domain}"
c2 = post(conn, "/sites/invitations/#{invitation.invitation_id}/accept")
assert redirected_to(c2) == "/sites"
assert Phoenix.Flash.get(c2.assigns.flash, :error) ==
"Invitation missing or already accepted"
end
end
describe "POST /sites/invitations/:invitation_id/accept - ownership transfer" do
test "downgrades previous owner to admin", %{conn: conn, user: user} do
old_owner = insert(:user)
site = insert(:site, members: [old_owner])
insert(:growth_subscription, user: user)
invitation =
insert(:invitation, site_id: site.id, inviter: old_owner, email: user.email, role: :owner)
conn = post(conn, "/sites/invitations/#{invitation.invitation_id}/accept")
assert redirected_to(conn, 302) == "/#{URI.encode_www_form(site.domain)}"
assert Phoenix.Flash.get(conn.assigns.flash, :success) =~
"You now have access to"
refute Repo.exists?(from(i in Plausible.Auth.Invitation, where: i.email == ^user.email))
old_owner_membership =
Repo.get_by(Plausible.Site.Membership, user_id: old_owner.id, site_id: site.id)
assert old_owner_membership.role == :admin
new_owner_membership =
Repo.get_by(Plausible.Site.Membership, user_id: user.id, site_id: site.id)
assert new_owner_membership.role == :owner
end
@tag :full_build_only
test "fails when new owner has no plan", %{conn: conn, user: user} do
old_owner = insert(:user)
site = insert(:site, members: [old_owner])
invitation =
insert(:invitation, site_id: site.id, inviter: old_owner, email: user.email, role: :owner)
conn = post(conn, "/sites/invitations/#{invitation.invitation_id}/accept")
assert redirected_to(conn, 302) == Routes.site_path(conn, :index)
assert Phoenix.Flash.get(conn.assigns.flash, :error) =~
"No existing subscription"
end
@tag :full_build_only
test "fails when new owner's plan is unsuitable", %{conn: conn, user: user} do
old_owner = insert(:user)
site = insert(:site, members: [old_owner])
insert(:growth_subscription, user: user)
# fill site limit quota
insert_list(10, :site, members: [user])
invitation =
insert(:invitation, site_id: site.id, inviter: old_owner, email: user.email, role: :owner)
conn = post(conn, "/sites/invitations/#{invitation.invitation_id}/accept")
assert redirected_to(conn, 302) == Routes.site_path(conn, :index)
assert Phoenix.Flash.get(conn.assigns.flash, :error) =~
"Plan limits exceeded: site limit."
end
end
describe "POST /sites/invitations/:invitation_id/reject" do
test "rejects the invitation", %{conn: conn, user: user} do
site = insert(:site)
invitation =
insert(:invitation,
site_id: site.id,
inviter: build(:user),
email: user.email,
role: :admin
)
conn = post(conn, "/sites/invitations/#{invitation.invitation_id}/reject")
assert redirected_to(conn, 302) == "/sites"
refute Repo.reload(invitation)
end
test "renders error for non-existent invitation", %{conn: conn} do
conn = post(conn, "/sites/invitations/does-not-exist/reject")
assert redirected_to(conn, 302) == "/sites"
assert Phoenix.Flash.get(conn.assigns.flash, :error) ==
"Invitation missing or already accepted"
end
end
describe "DELETE /sites/:website/invitations/:invitation_id" do
test "removes the invitation", %{conn: conn, user: user} do
site = insert(:site, memberships: [build(:site_membership, user: user, role: :admin)])
invitation =
insert(:invitation,
site_id: site.id,
inviter: build(:user),
email: "jane@example.com",
role: :admin
)
conn =
delete(
conn,
Routes.invitation_path(conn, :remove_invitation, site.domain, invitation.invitation_id)
)
assert redirected_to(conn, 302) == "/#{URI.encode_www_form(site.domain)}/settings/people"
refute Repo.reload(invitation)
end
test "fails to remove an invitation with insufficient permission", %{conn: conn, user: user} do
site = insert(:site, memberships: [build(:site_membership, user: user, role: :viewer)])
invitation =
insert(:invitation,
site_id: site.id,
inviter: build(:user),
email: "jane@example.com",
role: :admin
)
delete(
conn,
Routes.invitation_path(conn, :remove_invitation, site.domain, invitation.invitation_id)
)
assert Repo.reload(invitation)
end
test "fails to remove an invitation from the outside", %{conn: my_conn, user: me} do
_my_site = insert(:site, memberships: [build(:site_membership, user: me, role: "owner")])
other_user = insert(:user)
other_site =
insert(:site, memberships: [build(:site_membership, user: other_user, role: "owner")])
invitation =
insert(:invitation,
site_id: other_site.id,
inviter: other_user,
email: "jane@example.com",
role: :admin
)
remove_invitation_path =
Routes.invitation_path(
my_conn,
:remove_invitation,
other_site.domain,
invitation.invitation_id
)
delete(my_conn, remove_invitation_path)
assert Repo.reload(invitation)
end
test "renders error for non-existent invitation", %{conn: conn, user: user} do
site = insert(:site, memberships: [build(:site_membership, user: user, role: :admin)])
remove_invitation_path =
Routes.invitation_path(
conn,
:remove_invitation,
site.domain,
"does_not_exist"
)
conn = delete(conn, remove_invitation_path)
assert redirected_to(conn, 302) == "/#{URI.encode_www_form(site.domain)}/settings/people"
assert Phoenix.Flash.get(conn.assigns.flash, :error) ==
"Invitation missing or already removed"
end
end
end