defmodule Plausible.Teams.Test do @moduledoc """ Convenience assertions for teams schema transition """ use Plausible alias Plausible.Repo alias Plausible.Teams import Ecto.Query use ExUnit.CaseTemplate import Plausible.Factory defmacro __using__(_) do quote do import Plausible.Teams.Test end end def set_current_team(conn, team) do Plug.Conn.put_session(conn, :current_team_id, team.identifier) end on_ee do def new_consolidated_view(team) do team = Teams.complete_setup(team) {:ok, site} = Plausible.ConsolidatedView.enable(team) site end end def new_site(args \\ []) do args = cond do user = args[:owner] -> {owner, args} = Keyword.pop(args, :owner) {:ok, team} = Teams.get_or_create(user) args |> Keyword.put(:owners, [owner]) |> Keyword.put(:team, team) args[:team] -> args true -> user = new_user() {:ok, team} = Teams.get_or_create(user) args |> Keyword.put(:team, team) end :site |> insert(args) end def new_user(args \\ []) do {team_args, args} = Keyword.pop(args, :team, []) {trial_expiry_date, args} = Keyword.pop(args, :trial_expiry_date) on_ee do args = Keyword.merge([type: :standard], args) end user = insert(:user, args) trial_expiry_date = if team_args != [] && !trial_expiry_date do Date.add(Date.utc_today(), 30) else trial_expiry_date end if trial_expiry_date do {:ok, team} = Teams.get_or_create(user) team_args = Keyword.merge(team_args, trial_expiry_date: trial_expiry_date) team |> Ecto.Changeset.change(team_args) |> Repo.update!() end Repo.preload(user, team_memberships: :team) end def team_of(subject, opts \\ []) def team_of(%{team_memberships: [%{role: :owner, team: %Teams.Team{} = team}]}, opts) do if opts[:with_subscription?] do Plausible.Teams.with_subscription(team) else team end end def team_of(user, opts) do case Plausible.Teams.get_by_owner(user) do {:ok, team} -> if opts[:with_subscription?] do Plausible.Teams.with_subscription(team) else team end _ -> nil end end def add_site_guest_or_team_member(site, args \\ []) do case Keyword.pop!(args, :membership_type) do {:site_guest, args} -> add_guest(site, args) {:team_member, args} -> add_member(site.team, args) end end def add_guest(site, args \\ []) do user = Keyword.get(args, :user, new_user()) role = Keyword.fetch!(args, :role) team = Repo.preload(site, :team).team team_membership = build(:team_membership, team: team, user: user, role: :guest) |> Repo.insert!( on_conflict: [set: [updated_at: NaiveDateTime.utc_now()]], conflict_target: [:team_id, :user_id], returning: true ) insert(:guest_membership, site: site, team_membership: team_membership, role: role) user |> Repo.preload(:team_memberships) end def add_member(team, args \\ []) do user = Keyword.get(args, :user, new_user()) role = Keyword.fetch!(args, :role) insert(:team_membership, team: team, user: user, role: role) user |> Repo.preload(:team_memberships) end def invite_guest(site, invitee_or_email, args \\ []) when not is_nil(invitee_or_email) do {role, args} = Keyword.pop!(args, :role) {inviter, args} = Keyword.pop!(args, :inviter) {team_invitation_args, args} = Keyword.pop(args, :team_invitation, []) team = Repo.preload(site, :team).team email = case invitee_or_email do %{email: email} -> email email when is_binary(email) -> email end team_invitation = insert( :team_invitation, Keyword.merge( [ team: team, email: email, inviter: inviter, role: :guest ], team_invitation_args ) ) insert( :guest_invitation, Keyword.merge( [ team_invitation: team_invitation, site: site, role: role ], args ) ) end def invite_member(team, invitee_or_email, args \\ []) when not is_nil(invitee_or_email) do {role, args} = Keyword.pop!(args, :role) {inviter, args} = Keyword.pop!(args, :inviter) email = case invitee_or_email do %{email: email} -> email email when is_binary(email) -> email end insert( :team_invitation, Keyword.merge( [ team: team, email: email, inviter: inviter, role: role ], args ) ) end def invite_transfer(site, invitee_or_email, args \\ []) do {inviter, args} = Keyword.pop!(args, :inviter) email = case invitee_or_email do %{email: email} -> email email when is_binary(email) -> email end insert( :site_transfer, Keyword.merge( [ email: email, site: site, initiator: inviter ], args ) ) end def revoke_membership(site, user) do Repo.delete_all( from tm in Plausible.Teams.Membership, where: tm.user_id == ^user.id and tm.team_id == ^site.team.id ) user |> Repo.preload(:team_memberships) end def subscribe_to_starter_plan(user, attrs \\ []) do {:ok, team} = Teams.get_or_create(user) attrs = Keyword.merge([team: team], attrs) insert(:starter_subscription, attrs) user end def subscribe_to_growth_plan(user_or_team, attrs \\ []) def subscribe_to_growth_plan(%Plausible.Auth.User{} = user, attrs) do {:ok, team} = Teams.get_or_create(user) attrs = Keyword.merge([team: team], attrs) subscribe_to_growth_plan(team, attrs) user end def subscribe_to_growth_plan(%Plausible.Teams.Team{} = team, attrs) do attrs = Keyword.merge([team: team], attrs) insert(:growth_subscription, attrs) team end def subscribe_to_business_plan(%Plausible.Auth.User{} = user) do {:ok, team} = Teams.get_or_create(user) subscribe_to_business_plan(team) user end def subscribe_to_business_plan(%Plausible.Teams.Team{} = team) do insert(:business_subscription, team: team) team end def subscribe_to_plan(user, paddle_plan_id, attrs \\ []) do {:ok, team} = Teams.get_or_create(user) attrs = Keyword.merge([team: team, paddle_plan_id: paddle_plan_id], attrs) insert(:subscription, attrs) user end def subscribe_to_enterprise_plan(user, attrs \\ []) do {:ok, team} = Teams.get_or_create(user) {subscription?, attrs} = Keyword.pop(attrs, :subscription?, true) {subscription_attrs, attrs} = Keyword.pop(attrs, :subscription, []) enterprise_plan = insert(:enterprise_plan, Keyword.merge([team: team], attrs)) if subscription? do insert( :subscription, Keyword.merge( [ team: team, paddle_plan_id: enterprise_plan.paddle_plan_id ], subscription_attrs ) ) end user end def assert_team_exists(user, team_id \\ nil) do assert %{team_memberships: memberships} = Repo.preload(user, team_memberships: :team) tm = case memberships do [tm] -> tm _ -> raise "Team doesn't exist for user #{user.id}" end assert tm.role == :owner assert tm.team.id if team_id do assert tm.team.id == team_id end tm.team end def assert_team_membership(user, team, role \\ :owner) do assert membership = Repo.get_by(Teams.Membership, team_id: team.id, user_id: user.id, role: role ) membership end def refute_team_member(user, team) do refute Repo.get_by(Teams.Membership, team_id: team.id, user_id: user.id ) end def assert_team_attached(site, team_id \\ nil) do assert site = %{team: team} = site |> Repo.reload!() |> Repo.preload([:team, :owners]) for owner <- site.owners do assert membership = assert_team_membership(owner, team) assert membership.team_id == team.id end if team_id do assert team.id == team_id end team end def assert_guest_invitation(team, site, email, role) do assert team_invitation = Repo.get_by(Plausible.Teams.Invitation, email: email, team_id: team.id, role: :guest ) assert Repo.get_by(Plausible.Teams.GuestInvitation, team_invitation_id: team_invitation.id, site_id: site.id, role: role ) end def refute_team_invitation(team, email) do refute Repo.get_by(Plausible.Teams.Invitation, email: email, team_id: team.id ) end def assert_site_transfer(site, %Plausible.Auth.User{} = user) do assert_site_transfer(site, user.email) end def assert_site_transfer(site, email) when is_binary(email) do assert Repo.get_by(Plausible.Teams.SiteTransfer, site_id: site.id, email: email ) end def assert_guest_membership(team, site, user, role) do assert team_membership = Repo.get_by(Plausible.Teams.Membership, user_id: user.id, team_id: team.id, role: :guest ) assert Repo.get_by(Plausible.Teams.GuestMembership, team_membership_id: team_membership.id, site_id: site.id, role: role ) end def assert_non_guest_membership(team, site, user) do assert team_membership = Repo.get_by(Plausible.Teams.Membership, user_id: user.id, team_id: team.id ) assert team_membership.role != :guest refute Repo.get_by(Plausible.Teams.GuestMembership, team_membership_id: team_membership.id, site_id: site.id ) end def subscription_of(%Plausible.Auth.User{} = user) do user |> team_of() |> subscription_of() end def subscription_of(team) do team |> Plausible.Teams.with_subscription() |> Map.fetch!(:subscription) end on_ee do def audited_entry(name, attrs \\ []) do attrs = Keyword.put(attrs, :name, name) case Plausible.Audit.list_entries(attrs) do [entry] -> entry _ -> raise "Expected audited entry #{inspect(attrs)} but only found #{inspect(Plausible.Audit.list_entries([]), pretty: true)}." end end def audited_entries(length, name, attrs \\ []) do attrs = Keyword.put(attrs, :name, name) case Plausible.Audit.list_entries(attrs) do entries when is_list(entries) and length(entries) == length -> entries _ -> raise "Expected audited entry #{inspect(attrs)} but only found #{inspect(Plausible.Audit.list_entries([]), pretty: true)}." end end end end