Switch reads to Teams schemas across the rest of the app (#4860)

* Pre-emptively introduce `site.team_owner` relation

* Drop null constraint on user_id from subscriptions and enterprise_plans

* Temporarily remove populating old schemas in Teams.Test

* Point to site.owner via new schema

* Switch more reads to teams schema WIP

* Fix AuhtorizeSiteAccess test

There's no need to translate `admin`<->`editor` here,
the redundancy is inlined wherever the plug is initialized.

* Fix regions test

* Fix main graph test

* Fix authorization test

* Try to rely on team for subscription/plans where applicable

* Test fixes

* Fix plans test

* Prep for CheckUsage changes

* Skip remaining CheckUsage tests for now

* Fix user deletion to account for team relations

* Fix HelpScout tests

* 💀 Modify ingestion to read team schemas

* Made all tests green except skipped ones

* Mute warnings about transferring site with no order

By making artificial site membership struct,
when reading data off team membership schema.

* Fix site removal test case

* Re-enable locked site tests, that don't have to rely on `SiteLocker`

* Format

* Revert "Mute warnings about transferring site with no order"

This reverts commit 0e45f8c9d9.

* Re-enable old models and fix remaining tests

* Use new factories in a long running minio test

* FIXME->TODO

* Fix remaining tests in legacy mode (no FF raised)

* oof

cc @zoldar

* Add missing definitions of editor role in FE code

* Remove no longer relevant comment about roles

* Fix JS formatting

* Always prioritize site transfers over memberships in sites list

* Fix misaligned "Reject" invitation button

* Fix site pinning when user is guest in multiple sites in team

* Fix subscription settings controller tests

---------

Co-authored-by: Adam Rutkowski <hq@mtod.org>
This commit is contained in:
Adrian Gruntkowski 2024-12-02 13:20:16 +01:00 committed by GitHub
parent badc477048
commit b86c2e715f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
86 changed files with 942 additions and 902 deletions

View File

@ -160,7 +160,9 @@ export default class SiteSwitcher extends React.Component {
renderSettingsLink() {
if (
['owner', 'admin', 'super_admin'].includes(this.props.currentUserRole)
['owner', 'admin', 'editor', 'super_admin'].includes(
this.props.currentUserRole
)
) {
return (
<React.Fragment>

View File

@ -43,7 +43,7 @@ export default function Behaviours({ importedDataInView }) {
const site = useSiteContext();
const user = useUserContext();
const adminAccess = ['owner', 'admin', 'super_admin'].includes(user.role)
const adminAccess = ['owner', 'admin', 'editor', 'super_admin'].includes(user.role)
const tabKey = storage.getDomainScopedStorageKey('behavioursTab', site.domain)
const funnelKey = storage.getDomainScopedStorageKey('behavioursTabFunnel', site.domain)
const [enabledModes, setEnabledModes] = useState(getEnabledModes())

View File

@ -4,7 +4,8 @@ import React, { createContext, ReactNode, useContext } from 'react'
export enum Role {
owner = 'owner',
admin = 'admin',
viewer = 'viewer'
viewer = 'viewer',
editor = 'editor'
}
const userContextDefaultValue = {

View File

@ -82,19 +82,28 @@ defmodule Plausible.HelpScout do
def get_details_for_emails(emails, customer_id) do
with {:ok, user} <- get_user(emails) do
set_mapping(customer_id, user.email)
user = Plausible.Users.with_subscription(user.id)
plan = Billing.Plans.get_subscription_plan(user.subscription)
{team, subscription, plan} =
case Plausible.Teams.get_by_owner(user) do
{:ok, team} ->
team = Plausible.Teams.with_subscription(team)
plan = Billing.Plans.get_subscription_plan(team.subscription)
{team, team.subscription, plan}
{:error, :no_team} ->
{nil, nil, nil}
end
{:ok,
%{
email: user.email,
notes: user.notes,
status_label: status_label(user),
status_label: status_label(team, subscription),
status_link:
Routes.kaffy_resource_url(PlausibleWeb.Endpoint, :show, :auth, :user, user.id),
plan_label: plan_label(user.subscription, plan),
plan_link: plan_link(user.subscription),
sites_count: Plausible.Sites.owned_sites_count(user),
plan_label: plan_label(subscription, plan),
plan_link: plan_link(subscription),
sites_count: Plausible.Teams.owned_sites_count(team),
sites_link:
Routes.kaffy_resource_url(PlausibleWeb.Endpoint, :index, :sites, :site,
search: user.email
@ -111,8 +120,9 @@ defmodule Plausible.HelpScout do
domain_query =
from(s in Plausible.Site,
inner_join: sm in assoc(s, :memberships),
where: sm.user_id == parent_as(:user).id and sm.role == :owner,
inner_join: t in assoc(s, :team),
inner_join: tm in assoc(t, :team_memberships),
where: tm.user_id == parent_as(:user).id and tm.role == :owner,
where: ilike(s.domain, ^search_term) or ilike(s.domain_changed_from, ^search_term),
select: 1
)
@ -123,7 +133,7 @@ defmodule Plausible.HelpScout do
like(u.email, ^search_term) or exists(domain_query)
)
|> limit(5)
|> select([user: u, site_membership: sm], %{email: u.email, sites_count: count(sm.id)})
|> select([user: u, sites: s], %{email: u.email, sites_count: count(s.id)})
|> Repo.all()
end
@ -137,31 +147,31 @@ defmodule Plausible.HelpScout do
])
end
defp status_label(user) do
subscription_active? = Billing.Subscriptions.active?(user.subscription)
trial? = Plausible.Users.on_trial?(user)
defp status_label(team, subscription) do
subscription_active? = Billing.Subscriptions.active?(subscription)
trial? = Plausible.Teams.on_trial?(team)
cond do
not subscription_active? and not trial? and is_nil(user.trial_expiry_date) ->
not subscription_active? and not trial? and (is_nil(team) or is_nil(team.trial_expiry_date)) ->
"None"
is_nil(user.subscription) and not trial? ->
is_nil(subscription) and not trial? ->
"Expired trial"
trial? ->
"Trial"
user.subscription.status == Subscription.Status.deleted() ->
subscription.status == Subscription.Status.deleted() ->
if subscription_active? do
"Pending cancellation"
else
"Canceled"
end
user.subscription.status == Subscription.Status.paused() ->
subscription.status == Subscription.Status.paused() ->
"Paused"
Plausible.Sites.owned_sites_locked?(user) ->
Plausible.Teams.owned_sites_locked?(team) ->
"Dashboard locked"
subscription_active? ->
@ -228,13 +238,14 @@ defmodule Plausible.HelpScout do
defp users_query() do
from(u in Plausible.Auth.User,
as: :user,
left_join: sm in assoc(u, :site_memberships),
on: sm.role == :owner,
as: :site_membership,
left_join: s in assoc(sm, :site),
as: :site,
left_join: tm in assoc(u, :team_memberships),
on: tm.role == :owner,
as: :team_memberships,
left_join: t in assoc(tm, :team),
left_join: s in assoc(t, :sites),
as: :sites,
group_by: u.id,
order_by: [desc: count(sm.id)]
order_by: [desc: count(s.id)]
)
end

View File

@ -95,23 +95,24 @@ defmodule Plausible.Auth do
def delete_user(user) do
Repo.transaction(fn ->
user = Repo.preload(user, site_memberships: :site)
case Plausible.Teams.get_by_owner(user) do
{:ok, team} ->
for site <- Plausible.Teams.owned_sites(team) do
Plausible.Site.Removal.run(site)
end
for membership <- user.site_memberships do
if membership.role == :owner do
Plausible.Site.Removal.run(membership.site)
end
Repo.delete_all(from s in Plausible.Billing.Subscription, where: s.team_id == ^team.id)
Repo.delete_all(
from(
sm in Plausible.Site.Membership,
where: sm.id == ^membership.id
Repo.delete_all(
from ep in Plausible.Billing.EnterprisePlan, where: ep.team_id == ^team.id
)
)
Repo.delete!(team)
_ ->
:skip
end
{:ok, team} = Plausible.Teams.get_or_create(user)
Repo.delete!(team)
Repo.delete!(user)
end)
end

View File

@ -123,18 +123,27 @@ defmodule Plausible.Auth.UserAdmin do
end
defp subscription_status(user) do
cond do
user.subscription ->
status_str =
PlausibleWeb.SettingsView.present_subscription_status(user.subscription.status)
team =
case Plausible.Teams.get_by_owner(user) do
{:ok, team} ->
Plausible.Teams.with_subscription(team)
if user.subscription.paddle_subscription_id do
{:safe, ~s(<a href="#{manage_url(user.subscription)}">#{status_str}</a>)}
{:error, :no_team} ->
nil
end
cond do
team && team.subscription ->
status_str =
PlausibleWeb.SettingsView.present_subscription_status(team.subscription.status)
if team.subscription.paddle_subscription_id do
{:safe, ~s(<a href="#{manage_url(team.subscription)}">#{status_str}</a>)}
else
status_str
end
Plausible.Users.on_trial?(user) ->
Plausible.Teams.on_trial?(team) ->
"On trial"
true ->

View File

@ -280,8 +280,8 @@ defmodule Plausible.Billing do
def paddle_api(), do: Application.fetch_env!(:plausible, :paddle_api)
def cancelled_subscription_notice_dismiss_id(%User{} = user) do
"subscription_cancelled__#{user.id}"
def cancelled_subscription_notice_dismiss_id(id) do
"subscription_cancelled__#{id}"
end
defp active_subscription_query(user) do

View File

@ -188,7 +188,7 @@ defmodule Plausible.Billing.Plans do
defp get_enterprise_plan(%Subscription{} = subscription) do
Repo.get_by(EnterprisePlan,
user_id: subscription.user_id,
team_id: subscription.team_id,
paddle_plan_id: subscription.paddle_plan_id
)
end

View File

@ -16,11 +16,11 @@ defmodule Plausible.Billing.Subscription do
:status,
:next_bill_amount,
:next_bill_date,
:user_id,
# :team_id,
:currency_code
]
@optional_fields [:last_bill_date, :team_id]
@optional_fields [:last_bill_date, :team_id, :user_id]
schema "subscriptions" do
field :paddle_subscription_id, :string

View File

@ -51,7 +51,8 @@ defmodule Plausible.Site do
has_one :weekly_report, Plausible.Site.WeeklyReport
has_one :monthly_report, Plausible.Site.MonthlyReport
has_one :ownership, Plausible.Site.Membership, where: [role: :owner]
has_one :owner, through: [:ownership, :user]
has_one :legacy_owner, through: [:ownership, :user]
has_one :owner, through: [:team, :owner]
# If `from_cache?` is set, the struct might be incomplete - see `Plausible.Site.Cache`.
# Use `Plausible.Repo.reload!(cached_site)` to pre-fill missing fields if

View File

@ -48,13 +48,13 @@ defmodule Plausible.Site.Cache do
def base_db_query() do
from s in Site,
left_join: rg in assoc(s, :revenue_goals),
inner_join: owner in assoc(s, :owner),
inner_join: team in assoc(s, :team),
select: {
s.domain,
s.domain_changed_from,
%{struct(s, ^@cached_schema_fields) | from_cache?: true}
},
preload: [revenue_goals: rg, owner: owner]
preload: [revenue_goals: rg, team: team]
end
@impl true

View File

@ -44,7 +44,7 @@ defmodule Plausible.Site.GateKeeper do
defp policy(domain, opts) when is_binary(domain) do
with from_cache <- Cache.get(domain, Keyword.get(opts, :cache_opts, [])),
site = %Site{owner: %{accept_traffic_until: accept_traffic_until}} <- from_cache do
site = %Site{team: %{accept_traffic_until: accept_traffic_until}} <- from_cache do
if not is_nil(accept_traffic_until) and
Date.after?(Date.utc_today(), accept_traffic_until) do
:payment_required

View File

@ -2,7 +2,7 @@ defmodule Plausible.Site.Membership do
use Ecto.Schema
import Ecto.Changeset
@roles [:owner, :admin, :viewer]
@roles [:owner, :admin, :editor, :viewer]
@type t() :: %__MODULE__{}

View File

@ -54,6 +54,29 @@ defmodule Plausible.Teams do
)
end
def owned_sites_locked?(nil) do
false
end
def owned_sites_locked?(team) do
Repo.exists?(
from s in Plausible.Site,
where: s.team_id == ^team.id,
where: s.locked == true
)
end
def owned_sites_count(nil), do: 0
def owned_sites_count(team) do
Repo.aggregate(
from(s in Plausible.Site,
where: s.team_id == ^team.id
),
:count
)
end
@doc """
Create (when necessary) and load team relation for provided site.
@ -107,11 +130,11 @@ defmodule Plausible.Teams do
|> Repo.update!()
end
def get_by_owner(user) do
def get_by_owner(user_id) when is_integer(user_id) do
result =
from(tm in Teams.Membership,
inner_join: t in assoc(tm, :team),
where: tm.user_id == ^user.id and tm.role == :owner,
where: tm.user_id == ^user_id and tm.role == :owner,
select: t,
order_by: t.id
)
@ -126,6 +149,10 @@ defmodule Plausible.Teams do
end
end
def get_by_owner(%Plausible.Auth.User{} = user) do
get_by_owner(user.id)
end
def last_subscription_join_query() do
from(subscription in last_subscription_query(),
where: subscription.team_id == parent_as(:team).id

View File

@ -18,7 +18,9 @@ defmodule Plausible.Teams.Adapter do
)
end
def switch(user, opts \\ []) do
def switch(switch_on, opts \\ [])
def switch(%Plausible.Auth.User{} = user, opts) do
team_fn = Keyword.fetch!(opts, :team_fn)
user_fn = Keyword.fetch!(opts, :user_fn)
@ -37,4 +39,10 @@ defmodule Plausible.Teams.Adapter do
user_fn.(user)
end
end
def switch(team_or_nil, opts) do
team_fn = Keyword.fetch!(opts, :team_fn)
team = Plausible.Teams.with_subscription(team_or_nil)
team_fn.(team)
end
end

View File

@ -259,6 +259,7 @@ defmodule Plausible.Teams.Adapter.Read.Sites do
where: tm.user_id == ^user_id,
where: coalesce(gm.role, tm.role) in ^roles,
where: s.domain == ^domain or s.domain_changed_from == ^domain,
where: is_nil(gm.id) or gm.site_id == s.id,
select: s
)
end

View File

@ -324,6 +324,8 @@ defmodule Plausible.Teams.Billing do
def features_usage(team, site_ids \\ nil)
def features_usage(nil, nil), do: []
def features_usage(%Teams.Team{} = team, nil) do
owned_site_ids = team |> Teams.owned_sites() |> Enum.map(& &1.id)
features_usage(team, owned_site_ids)

View File

@ -56,6 +56,8 @@ defmodule Plausible.Teams.Memberships do
end
end
def site_role(_site, nil), do: {:error, :not_a_member}
def site_role(site, user) do
result =
from(u in Auth.User,
@ -74,6 +76,13 @@ defmodule Plausible.Teams.Memberships do
end
end
def site_member?(site, user) do
case site_role(site, user) do
{:ok, _} -> true
_ -> false
end
end
def update_role_sync(site_membership) do
site_id = site_membership.site_id
user_id = site_membership.user_id

View File

@ -129,8 +129,17 @@ defmodule Plausible.Teams.Sites do
team_membership_query =
from tm in Teams.Membership,
inner_join: t in assoc(tm, :team),
inner_join: u in assoc(tm, :user),
as: :user,
inner_join: s in assoc(t, :sites),
as: :site,
where: tm.user_id == ^user.id and tm.role != :guest,
where:
not exists(
from st in Teams.SiteTransfer,
where: st.email == parent_as(:user).email,
where: st.site_id == parent_as(:site).id
),
select: %{
site_id: s.id,
entry_type: "site",
@ -142,9 +151,18 @@ defmodule Plausible.Teams.Sites do
guest_membership_query =
from(tm in Teams.Membership,
inner_join: u in assoc(tm, :user),
as: :user,
inner_join: gm in assoc(tm, :guest_memberships),
inner_join: s in assoc(gm, :site),
as: :site,
where: tm.user_id == ^user.id and tm.role == :guest,
where:
not exists(
from st in Teams.SiteTransfer,
where: st.email == parent_as(:user).email,
where: st.site_id == parent_as(:site).id
),
select: %{
site_id: s.id,
entry_type: "site",
@ -251,12 +269,14 @@ defmodule Plausible.Teams.Sites do
fragment(
"""
CASE
WHEN ? IS NOT NULL THEN 'invitation'
WHEN ? IS NOT NULL THEN 'invitation'
WHEN ? IS NOT NULL THEN 'pinned_site'
ELSE ?
END
""",
gi.id,
st.id,
up.pinned_at,
u.entry_type
),

View File

@ -26,6 +26,9 @@ defmodule Plausible.Teams.Team do
has_one :subscription, Plausible.Billing.Subscription
has_one :enterprise_plan, Plausible.Billing.EnterprisePlan
has_one :ownership, Plausible.Teams.Membership, where: [role: :owner]
has_one :owner, through: [:ownership, :user]
timestamps()
end

View File

@ -63,6 +63,7 @@ defmodule PlausibleWeb.Components.Billing.Notice do
attr(:billable_user, User, required: true)
attr(:current_user, User, required: true)
attr(:current_team, Plausible.Teams.Team, required: true)
attr(:feature_mod, :atom, required: true, values: Feature.list())
attr(:grandfathered?, :boolean, default: false)
attr(:rest, :global)
@ -76,13 +77,18 @@ defmodule PlausibleWeb.Components.Billing.Notice do
{@rest}
>
<%= account_label(@current_user, @billable_user) %> does not have access to <%= @feature_mod.display_name() %>. To get access to this feature,
<.upgrade_call_to_action current_user={@current_user} billable_user={@billable_user} />.
<.upgrade_call_to_action
current_team={@current_team}
current_user={@current_user}
billable_user={@billable_user}
/>.
</.notice>
"""
end
attr(:billable_user, User, required: true)
attr(:current_user, User, required: true)
attr(:current_team, Plausible.Teams.Team, required: true)
attr(:limit, :integer, required: true)
attr(:resource, :string, required: true)
attr(:rest, :global)
@ -91,12 +97,16 @@ defmodule PlausibleWeb.Components.Billing.Notice do
~H"""
<.notice {@rest} title="Notice">
<%= account_label(@current_user, @billable_user) %> is limited to <%= @limit %> <%= @resource %>. To increase this limit,
<.upgrade_call_to_action current_user={@current_user} billable_user={@billable_user} />.
<.upgrade_call_to_action
current_team={@current_team}
current_user={@current_user}
billable_user={@billable_user}
/>.
</.notice>
"""
end
attr(:user, :map, required: true)
attr(:subscription, :map, required: true)
attr(:dismissable, :boolean, default: true)
@doc """
@ -117,18 +127,18 @@ defmodule PlausibleWeb.Components.Billing.Notice do
def subscription_cancelled(
%{
dismissable: true,
user: %User{subscription: %Subscription{status: Subscription.Status.deleted()}}
subscription: %Subscription{status: Subscription.Status.deleted()}
} = assigns
) do
~H"""
<aside id="global-subscription-cancelled-notice" class="container">
<.notice
dismissable_id={Plausible.Billing.cancelled_subscription_notice_dismiss_id(@user)}
dismissable_id={Plausible.Billing.cancelled_subscription_notice_dismiss_id(@subscription.id)}
title="Subscription cancelled"
theme={:red}
class="shadow-md dark:shadow-none"
>
<.subscription_cancelled_notice_body user={@user} />
<.subscription_cancelled_notice_body subscription={@subscription} />
</.notice>
</aside>
"""
@ -137,7 +147,7 @@ defmodule PlausibleWeb.Components.Billing.Notice do
def subscription_cancelled(
%{
dismissable: false,
user: %User{subscription: %Subscription{status: Subscription.Status.deleted()}}
subscription: %Subscription{status: Subscription.Status.deleted()}
} = assigns
) do
assigns = assign(assigns, :container_id, "local-subscription-cancelled-notice")
@ -145,11 +155,11 @@ defmodule PlausibleWeb.Components.Billing.Notice do
~H"""
<aside id={@container_id} class="hidden">
<.notice title="Subscription cancelled" theme={:red} class="shadow-md dark:shadow-none">
<.subscription_cancelled_notice_body user={@user} />
<.subscription_cancelled_notice_body subscription={@subscription} />
</.notice>
</aside>
<script
data-localstorage-key={"notice_dismissed__#{Plausible.Billing.cancelled_subscription_notice_dismiss_id(assigns.user)}"}
data-localstorage-key={"notice_dismissed__#{Plausible.Billing.cancelled_subscription_notice_dismiss_id(@subscription.id)}"}
data-container-id={@container_id}
>
const dataset = document.currentScript.dataset
@ -251,7 +261,7 @@ defmodule PlausibleWeb.Components.Billing.Notice do
end
defp subscription_cancelled_notice_body(assigns) do
if Subscriptions.expired?(assigns.user.subscription) do
if Subscriptions.expired?(assigns.subscription) do
~H"""
<.link
class="underline inline-block"
@ -264,7 +274,7 @@ defmodule PlausibleWeb.Components.Billing.Notice do
else
~H"""
<p>
You have access to your stats until <span class="font-semibold inline"><%= Calendar.strftime(@user.subscription.next_bill_date, "%b %-d, %Y") %></span>.
You have access to your stats until <span class="font-semibold inline"><%= Calendar.strftime(@subscription.next_bill_date, "%b %-d, %Y") %></span>.
<.link
class="underline inline-block"
href={Routes.billing_path(PlausibleWeb.Endpoint, :choose_plan)}
@ -273,12 +283,12 @@ defmodule PlausibleWeb.Components.Billing.Notice do
</.link>
to make sure you don't lose access.
</p>
<.lose_grandfathering_warning user={@user} />
<.lose_grandfathering_warning subscription={@subscription} />
"""
end
end
defp lose_grandfathering_warning(%{user: %{subscription: subscription}} = assigns) do
defp lose_grandfathering_warning(%{subscription: subscription} = assigns) do
plan = Plans.get_regular_plan(subscription, only_non_expired: true)
loses_grandfathering = plan && plan.generation < 4
@ -297,13 +307,14 @@ defmodule PlausibleWeb.Components.Billing.Notice do
end
attr(:current_user, :map)
attr(:current_team, :map)
attr(:billable_user, :map)
defp upgrade_call_to_action(assigns) do
billable_user = Plausible.Users.with_subscription(assigns.billable_user)
team = Plausible.Teams.with_subscription(assigns.current_team)
upgrade_assistance_required? =
case Plans.get_subscription_plan(billable_user.subscription) do
case Plans.get_subscription_plan(team.subscription) do
%Plausible.Billing.Plan{kind: :business} -> true
%Plausible.Billing.EnterprisePlan{} -> true
_ -> false

View File

@ -225,7 +225,7 @@ defmodule PlausibleWeb.Components.Billing.PlanBox do
|> assign(:confirm_message, losing_features_message(feature_usage_check))
~H"""
<%= if @owned_plan && Plausible.Billing.Subscriptions.resumable?(@current_user.subscription) do %>
<%= if @owned_plan && Plausible.Billing.Subscriptions.resumable?(@current_team.subscription) do %>
<.change_plan_link {assigns} />
<% else %>
<PlausibleWeb.Components.Billing.paddle_button

View File

@ -1,23 +1,29 @@
defmodule PlausibleWeb.AdminController do
use PlausibleWeb, :controller
alias Plausible.Billing.Quota
alias Plausible.Teams
def usage(conn, params) do
user =
params["user_id"]
|> String.to_integer()
|> Plausible.Users.with_subscription()
user_id = String.to_integer(params["user_id"])
usage = Quota.Usage.usage(user, with_features: true)
team =
case Teams.get_by_owner(user_id) do
{:ok, team} ->
Teams.with_subscription(team)
{:error, :no_team} ->
nil
end
usage = Teams.Billing.quota_usage(team, with_features: true)
limits = %{
monthly_pageviews: Quota.Limits.monthly_pageview_limit(user.subscription),
sites: Quota.Limits.site_limit(user),
team_members: Quota.Limits.team_member_limit(user)
monthly_pageviews: Teams.Billing.monthly_pageview_limit(team),
sites: Teams.Billing.site_limit(team),
team_members: Teams.Billing.team_member_limit(team)
}
html_response = usage_and_limits_html(user, usage, limits, params["embed"] == "true")
html_response = usage_and_limits_html(team, usage, limits, params["embed"] == "true")
conn
|> put_resp_content_type("text/html")
@ -25,14 +31,20 @@ defmodule PlausibleWeb.AdminController do
end
def current_plan(conn, params) do
user =
params["user_id"]
|> String.to_integer()
|> Plausible.Users.with_subscription()
user_id = String.to_integer(params["user_id"])
team =
case Teams.get_by_owner(user_id) do
{:ok, team} ->
Teams.with_subscription(team)
{:error, :no_team} ->
nil
end
plan =
case user && user.subscription &&
Plausible.Billing.Plans.get_subscription_plan(user.subscription) do
case team && team.subscription &&
Plausible.Billing.Plans.get_subscription_plan(team.subscription) do
%{} = plan ->
plan
|> Map.take([
@ -56,9 +68,10 @@ defmodule PlausibleWeb.AdminController do
|> send_resp(200, json_response)
end
defp usage_and_limits_html(user, usage, limits, embed?) do
defp usage_and_limits_html(team, usage, limits, embed?) do
content = """
<ul>
<li>Team: <b>#{team && team.name}</b></li>
<li>Sites: <b>#{usage.sites}</b> / #{limits.sites}</li>
<li>Team members: <b>#{usage.team_members}</b> / #{limits.team_members}</li>
<li>Features: #{features_usage(usage.features)}</li>
@ -76,7 +89,7 @@ defmodule PlausibleWeb.AdminController do
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Usage - user:#{user.id}</title>
<title>Usage - team:#{team.id}</title>
<style>
ul, li {margin-top: 10px;}
body {padding-top: 10px;}

View File

@ -4,7 +4,7 @@ defmodule PlausibleWeb.BillingController do
require Logger
require Plausible.Billing.Subscription.Status
alias Plausible.Billing
alias Plausible.Billing.{Plans, Subscription}
alias Plausible.Billing.Subscription
plug PlausibleWeb.RequireAccountPlug
@ -30,10 +30,14 @@ defmodule PlausibleWeb.BillingController do
def upgrade_to_enterprise_plan(conn, _params) do
current_user = conn.assigns.current_user
current_team = conn.assigns.current_team
subscription = Plausible.Teams.Adapter.Read.Billing.get_subscription(current_user)
{latest_enterprise_plan, price} =
Plans.latest_enterprise_plan_with_price(current_user, PlausibleWeb.RemoteIP.get(conn))
Plausible.Teams.Billing.latest_enterprise_plan_with_price(
current_team,
PlausibleWeb.RemoteIP.get(conn)
)
subscription_resumable? =
Plausible.Billing.Subscriptions.resumable?(subscription)

View File

@ -8,7 +8,7 @@ defmodule PlausibleWeb.GoogleAnalyticsController do
plug(PlausibleWeb.RequireAccountPlug)
plug(PlausibleWeb.Plugs.AuthorizeSiteAccess, [:owner, :admin, :super_admin])
plug(PlausibleWeb.Plugs.AuthorizeSiteAccess, [:owner, :editor, :admin, :super_admin])
def property_form(
conn,

View File

@ -4,7 +4,7 @@ defmodule PlausibleWeb.InvitationController do
plug PlausibleWeb.RequireAccountPlug
plug PlausibleWeb.Plugs.AuthorizeSiteAccess,
[:owner, :admin] when action in [:remove_invitation]
[:owner, :editor, :admin] when action in [:remove_invitation]
def accept_invitation(conn, %{"invitation_id" => invitation_id}) do
case Plausible.Site.Memberships.accept_invitation(invitation_id, conn.assigns.current_user) do

View File

@ -21,7 +21,7 @@ defmodule PlausibleWeb.Site.MembershipController do
plug PlausibleWeb.Plugs.AuthorizeSiteAccess, [:owner] when action in @only_owner_is_allowed_to
plug PlausibleWeb.Plugs.AuthorizeSiteAccess,
[:owner, :admin] when action not in @only_owner_is_allowed_to
[:owner, :editor, :admin] when action not in @only_owner_is_allowed_to
def invite_member_form(conn, _params) do
site =
@ -198,12 +198,16 @@ defmodule PlausibleWeb.Site.MembershipController do
end
end
defp can_grant_role_to_self?(:editor, :viewer), do: true
defp can_grant_role_to_self?(:admin, :viewer), do: true
defp can_grant_role_to_self?(_, _), do: false
defp can_grant_role_to_other?(:owner, :editor), do: true
defp can_grant_role_to_other?(:owner, :admin), do: true
defp can_grant_role_to_other?(:owner, :viewer), do: true
defp can_grant_role_to_other?(:editor, :editor), do: true
defp can_grant_role_to_other?(:admin, :admin), do: true
defp can_grant_role_to_other?(:editor, :viewer), do: true
defp can_grant_role_to_other?(:admin, :viewer), do: true
defp can_grant_role_to_other?(_, _), do: false

View File

@ -9,7 +9,7 @@ defmodule PlausibleWeb.SiteController do
plug(
PlausibleWeb.Plugs.AuthorizeSiteAccess,
[:owner, :admin, :super_admin] when action not in [:new, :create_site]
[:owner, :admin, :editor, :super_admin] when action not in [:new, :create_site]
)
def new(conn, params) do

View File

@ -60,6 +60,7 @@ defmodule PlausibleWeb.Live.GoalSettings do
domain={@domain}
site={@site}
current_user={@current_user}
current_team={@current_team}
existing_goals={@all_goals}
goal={@form_goal}
on_save_goal={

View File

@ -37,6 +37,7 @@ defmodule PlausibleWeb.Live.GoalSettings.Form do
event_name_options_count: length(assigns.event_name_options),
event_name_options: Enum.map(assigns.event_name_options, &{&1, &1}),
current_user: assigns.current_user,
current_team: assigns.current_team,
domain: assigns.domain,
selected_tab: selected_tab,
tab_sequence_id: 0,
@ -73,6 +74,7 @@ defmodule PlausibleWeb.Live.GoalSettings.Form do
f={f}
suffix={@context_unique_id}
current_user={@current_user}
current_team={@current_team}
site={@site}
goal={@goal}
existing_goals={@existing_goals}
@ -115,6 +117,7 @@ defmodule PlausibleWeb.Live.GoalSettings.Form do
f={f}
suffix={suffix(@context_unique_id, @tab_sequence_id)}
current_user={@current_user}
current_team={@current_team}
site={@site}
existing_goals={@existing_goals}
goal_options={@event_name_options}
@ -199,6 +202,7 @@ defmodule PlausibleWeb.Live.GoalSettings.Form do
attr(:f, Phoenix.HTML.Form)
attr(:site, Plausible.Site)
attr(:current_user, Plausible.Auth.User)
attr(:current_team, Plausible.Teams.Team)
attr(:suffix, :string)
attr(:existing_goals, :list)
attr(:goal_options, :list)
@ -259,6 +263,7 @@ defmodule PlausibleWeb.Live.GoalSettings.Form do
f={@f}
site={@site}
current_user={@current_user}
current_team={@current_team}
has_access_to_revenue_goals?={@has_access_to_revenue_goals?}
goal={@goal}
suffix={@suffix}
@ -282,6 +287,7 @@ defmodule PlausibleWeb.Live.GoalSettings.Form do
<PlausibleWeb.Components.Billing.Notice.premium_feature
billable_user={@site.owner}
current_user={@current_user}
current_team={@current_team}
feature_mod={Plausible.Billing.Feature.RevenueGoals}
class="rounded-b-md"
/>

View File

@ -499,15 +499,15 @@ defmodule PlausibleWeb.Live.Sites do
>
Upgrade
</.button_link>
<button
type="button"
class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 dark:border-gray-500 shadow-sm px-4 py-2 bg-white dark:bg-gray-800 text-base font-medium text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-850 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm"
<.button_link
href="#"
theme="bright"
data-method="post"
data-csrf={Plug.CSRFProtection.get_csrf_token()}
x-bind:data-to="selectedInvitation && ('/sites/invitations/' + selectedInvitation.invitation.invitation_id + '/reject')"
>
Reject
</button>
</.button_link>
</div>
</div>
</div>

View File

@ -134,7 +134,7 @@ defmodule PlausibleWeb.Plugs.AuthorizePublicAPI do
end
defp verify_site_access(api_key, site) do
is_member? = Sites.is_member?(api_key.user_id, site)
is_member? = Plausible.Teams.Memberships.site_member?(site, api_key.user)
is_super_admin? = Auth.is_super_admin?(api_key.user_id)
cond do
@ -144,7 +144,8 @@ defmodule PlausibleWeb.Plugs.AuthorizePublicAPI do
Sites.locked?(site) ->
{:error, :site_locked}
Plausible.Billing.Feature.StatsAPI.check_availability(api_key.user) !== :ok ->
Plausible.Teams.Adapter.Read.Billing.check_feature_availability_for_stats_api(api_key.user) !==
:ok ->
{:error, :upgrade_required}
is_member? ->

View File

@ -38,7 +38,7 @@ defmodule PlausibleWeb.Plugs.AuthorizeSiteAccess do
import Plug.Conn
import Phoenix.Controller, only: [get_format: 1]
@all_roles [:public, :viewer, :admin, :super_admin, :owner]
@all_roles [:public, :viewer, :admin, :editor, :super_admin, :owner]
def init([]), do: {@all_roles, nil}
@ -147,29 +147,18 @@ defmodule PlausibleWeb.Plugs.AuthorizeSiteAccess do
end
defp get_site_with_role(conn, current_user, domain) do
site_query =
from(
s in Plausible.Site,
where: s.domain == ^domain,
select: %{site: s}
)
site = Repo.get_by(Plausible.Site, domain: domain)
full_query =
if current_user do
from(s in site_query,
left_join: sm in Plausible.Site.Membership,
on: sm.site_id == s.id and sm.user_id == ^current_user.id,
select_merge: %{role: sm.role}
)
else
from(s in site_query,
select_merge: %{role: nil}
)
end
if site do
site_role =
case Plausible.Teams.Memberships.site_role(site, current_user) do
{:ok, role} -> role
_ -> nil
end
case Repo.one(full_query) do
%{site: _site} = result -> {:ok, result}
_ -> error_not_found(conn)
{:ok, %{site: site, role: site_role}}
else
error_not_found(conn)
end
end

View File

@ -50,7 +50,10 @@ defmodule PlausibleWeb.Router do
plug :accepts, ["json"]
plug :fetch_session
plug PlausibleWeb.AuthPlug
plug PlausibleWeb.Plugs.AuthorizeSiteAccess, {[:admin, :super_admin, :owner], "site_id"}
plug PlausibleWeb.Plugs.AuthorizeSiteAccess,
{[:admin, :editor, :super_admin, :owner], "site_id"}
plug PlausibleWeb.Plugs.NoRobots
end

View File

@ -2,27 +2,27 @@
<%= render("_flash.html", assigns) %>
<% end %>
<%= if @conn.assigns[:current_user] do %>
<%= if @conn.assigns[:current_team] do %>
<div class="flex flex-col gap-y-2">
<Notice.active_grace_period
:if={Plausible.Auth.GracePeriod.active?(@conn.assigns.current_user)}
:if={Plausible.Auth.GracePeriod.active?(@conn.assigns.current_team)}
enterprise?={
Plausible.Teams.Adapter.Read.Billing.enterprise_configured?(@conn.assigns.current_user)
Plausible.Teams.Adapter.Read.Billing.enterprise_configured?(@conn.assigns.current_team)
}
grace_period_end={grace_period_end(@conn.assigns.current_user)}
grace_period_end={grace_period_end(@conn.assigns.current_team)}
/>
<Notice.dashboard_locked :if={Plausible.Auth.GracePeriod.expired?(@conn.assigns.current_user)} />
<Notice.dashboard_locked :if={Plausible.Auth.GracePeriod.expired?(@conn.assigns.current_team)} />
<Notice.subscription_cancelled user={@conn.assigns.current_user} />
<Notice.subscription_cancelled subscription={@conn.assigns.current_team.subscription} />
<Notice.subscription_past_due
subscription={@conn.assigns.current_user.subscription}
subscription={@conn.assigns.current_team.subscription}
class="container"
/>
<Notice.subscription_paused
subscription={@conn.assigns.current_user.subscription}
subscription={@conn.assigns.current_team.subscription}
class="container"
/>
</div>

View File

@ -10,6 +10,7 @@
<PlausibleWeb.Components.Billing.Notice.premium_feature
billable_user={@current_user}
current_user={@current_user}
current_team={@current_team}
feature_mod={Plausible.Billing.Feature.StatsAPI}
/>

View File

@ -7,7 +7,7 @@
<:title>Delete Account</:title>
<:subtitle>Deleting your account removes all sites and stats you've collected</:subtitle>
<%= if Plausible.Billing.Subscription.Status.active?(@current_user.subscription) do %>
<%= if Plausible.Billing.Subscription.Status.active?(@current_team && @current_team.subscription) do %>
<.notice theme={:gray} title="Cannot delete account at this time">
Your account cannot be deleted because you have an active subscription. If you want to delete your account, please cancel your subscription first.
</.notice>

View File

@ -27,7 +27,7 @@
</div>
<PlausibleWeb.Components.Billing.Notice.subscription_cancelled
user={@current_user}
subscription={@subscription}
dismissable={false}
/>

View File

@ -13,6 +13,7 @@
:if={Map.get(assigns, :is_at_limit, false)}
current_user={@current_user}
billable_user={@site.owner}
current_team={@current_team}
limit={Map.get(assigns, :team_member_limit, 0)}
resource="team members"
/>

View File

@ -9,6 +9,7 @@
<PlausibleWeb.Components.Billing.Notice.limit_exceeded
:if={@site_limit_exceeded?}
current_user={@current_user}
current_team={@current_team}
billable_user={@current_user}
limit={@site_limit}
resource="sites"

View File

@ -15,6 +15,7 @@
<PlausibleWeb.Components.Billing.Notice.premium_feature
billable_user={@site.owner}
current_user={@current_user}
current_team={@current_team}
feature_mod={Plausible.Billing.Feature.Funnels}
/>

View File

@ -16,6 +16,7 @@
<PlausibleWeb.Components.Billing.Notice.premium_feature
billable_user={@site.owner}
current_user={@current_user}
current_team={@current_team}
feature_mod={Plausible.Billing.Feature.Props}
grandfathered?
/>

View File

@ -40,7 +40,7 @@
Manage my subscription
</.button_link>
</div>
<% role when role in [:admin, :viewer] -> %>
<% role when role in [:admin, :viewer, :editor] -> %>
<div class="mt-3 text-gray-600 dark:text-gray-300 text-center">
<p>
This dashboard is currently locked and cannot be accessed. The site owner

View File

@ -131,6 +131,8 @@ defmodule PlausibleWeb.UserAuth do
inner_join: u in assoc(us, :user),
as: :user,
left_join: tm in assoc(u, :team_memberships),
# TODO: whenever current_team.subscription is used to prevent user action, we must check whether the team association is ownership.
# Otherwise regular members will be limited by team owner in cases like deleting their own account.
on: tm.role != :guest,
left_join: t in assoc(tm, :team),
as: :team,

View File

@ -9,12 +9,13 @@
#
# We recommend using the bang functions (`insert!`, `update!`
# and so on) as they will fail if something goes wrong.
import Plausible.Teams.Test
words =
for i <- 0..(:erlang.system_info(:atom_count) - 1),
do: :erlang.binary_to_term(<<131, 75, i::24>>)
user = Plausible.Factory.insert(:user, email: "user@plausible.test", password: "plausible")
user = new_user(email: "user@plausible.test", password: "plausible")
native_stats_range =
Date.range(
@ -51,19 +52,17 @@ long_random_urls =
end
site =
Plausible.Factory.insert(:site,
new_site(
domain: "dummy.site",
native_stats_start_at: NaiveDateTime.new!(native_stats_range.first, ~T[00:00:00]),
stats_start_date: NaiveDateTime.new!(legacy_imported_stats_range.first, ~T[00:00:00]),
memberships: [
Plausible.Factory.build(:site_membership, user: user, role: :owner),
Plausible.Factory.build(:site_membership,
user: Plausible.Factory.build(:user, name: "Arnold Wallaby", password: "plausible"),
role: :viewer
)
]
team: [
native_stats_start_at: NaiveDateTime.new!(native_stats_range.first, ~T[00:00:00]),
stats_start_date: NaiveDateTime.new!(legacy_imported_stats_range.first, ~T[00:00:00])
],
owner: user
)
add_guest(site, user: new_user(name: "Arnold Wallaby", password: "plausible"), role: :viewer)
Plausible.Factory.insert_list(29, :ip_rule, site: site)
Plausible.Factory.insert(:country_rule, site: site, country_code: "PL")
Plausible.Factory.insert(:country_rule, site: site, country_code: "EE")

View File

@ -24,7 +24,7 @@ defmodule Plausible.Billing.FeatureTest do
end
test "#{mod}.check_availability/1 returns error when site owner is on an old plan" do
user = insert(:user, subscription: build(:subscription, paddle_plan_id: @v1_plan_id))
user = new_user() |> subscribe_to_plan(@v1_plan_id)
assert {:error, :upgrade_required} == unquote(mod).check_availability(user)
end
end

View File

@ -12,6 +12,7 @@ defmodule Plausible.Billing.PlansTest do
test "growth_plans_for/1 returns v1 plans for a user on a legacy plan" do
new_user()
|> subscribe_to_plan(@legacy_plan_id)
|> team_of(with_subscription?: true)
|> Map.fetch!(:subscription)
|> Plans.growth_plans_for()
|> assert_generation(1)
@ -20,6 +21,7 @@ defmodule Plausible.Billing.PlansTest do
test "growth_plans_for/1 returns v1 plans for users who are already on v1 pricing" do
new_user()
|> subscribe_to_plan(@v1_plan_id)
|> team_of(with_subscription?: true)
|> Map.fetch!(:subscription)
|> Plans.growth_plans_for()
|> assert_generation(1)
@ -28,6 +30,7 @@ defmodule Plausible.Billing.PlansTest do
test "growth_plans_for/1 returns v2 plans for users who are already on v2 pricing" do
new_user()
|> subscribe_to_plan(@v2_plan_id)
|> team_of(with_subscription?: true)
|> Map.fetch!(:subscription)
|> Plans.growth_plans_for()
|> assert_generation(2)
@ -36,6 +39,7 @@ defmodule Plausible.Billing.PlansTest do
test "growth_plans_for/1 returns v4 plans for expired legacy subscriptions" do
new_user()
|> subscribe_to_plan(@v1_plan_id, status: :deleted, next_bill_date: ~D[2023-11-10])
|> team_of(with_subscription?: true)
|> Map.fetch!(:subscription)
|> Plans.growth_plans_for()
|> assert_generation(4)
@ -43,7 +47,7 @@ defmodule Plausible.Billing.PlansTest do
test "growth_plans_for/1 shows v4 plans for everyone else" do
new_user()
|> Repo.preload(:subscription)
|> team_of(with_subscription?: true)
|> Map.fetch!(:subscription)
|> Plans.growth_plans_for()
|> assert_generation(4)
@ -51,7 +55,7 @@ defmodule Plausible.Billing.PlansTest do
test "growth_plans_for/1 does not return business plans" do
new_user()
|> Repo.preload(:subscription)
|> team_of(with_subscription?: true)
|> Map.fetch!(:subscription)
|> Plans.growth_plans_for()
|> Enum.each(fn plan ->
@ -62,6 +66,7 @@ defmodule Plausible.Billing.PlansTest do
test "growth_plans_for/1 returns the latest generation of growth plans for a user with a business subscription" do
new_user()
|> subscribe_to_plan(@v3_business_plan_id)
|> team_of(with_subscription?: true)
|> Map.fetch!(:subscription)
|> Plans.growth_plans_for()
|> assert_generation(4)
@ -70,6 +75,7 @@ defmodule Plausible.Billing.PlansTest do
test "business_plans_for/1 returns v3 business plans for a user on a legacy plan" do
new_user()
|> subscribe_to_plan(@legacy_plan_id)
|> team_of(with_subscription?: true)
|> Map.fetch!(:subscription)
|> Plans.business_plans_for()
|> assert_generation(3)
@ -78,7 +84,9 @@ defmodule Plausible.Billing.PlansTest do
test "business_plans_for/1 returns v3 business plans for a v2 subscriber" do
user = new_user() |> subscribe_to_plan(@v2_plan_id)
business_plans = Plans.business_plans_for(user.subscription)
subscription = team_of(user, with_subscription?: true).subscription
business_plans = Plans.business_plans_for(subscription)
assert Enum.all?(business_plans, &(&1.kind == :business))
assert_generation(business_plans, 3)
@ -97,14 +105,22 @@ defmodule Plausible.Billing.PlansTest do
new_user()
|> subscribe_to_plan(@v2_plan_id, status: :deleted, next_bill_date: ~D[2023-11-10])
user.subscription
user
|> team_of(with_subscription?: true)
|> Map.fetch!(:subscription)
|> Plans.business_plans_for()
|> assert_generation(4)
end
test "business_plans_for/1 returns v4 business plans for everyone else" do
user = new_user() |> Repo.preload(:subscription)
business_plans = Plans.business_plans_for(user.subscription)
user = new_user()
subscription =
user
|> team_of(with_subscription?: true)
|> Map.fetch!(:subscription)
business_plans = Plans.business_plans_for(subscription)
assert Enum.all?(business_plans, &(&1.kind == :business))
assert_generation(business_plans, 4)
@ -113,8 +129,13 @@ defmodule Plausible.Billing.PlansTest do
test "available_plans returns all plans for user with prices when asked for" do
user = new_user() |> subscribe_to_plan(@v2_plan_id)
subscription =
user
|> team_of(with_subscription?: true)
|> Map.fetch!(:subscription)
%{growth: growth_plans, business: business_plans} =
Plans.available_plans_for(user.subscription, with_prices: true, customer_ip: "127.0.0.1")
Plans.available_plans_for(subscription, with_prices: true, customer_ip: "127.0.0.1")
assert Enum.find(growth_plans, fn plan ->
(%Money{} = plan.monthly_cost) && plan.monthly_product_id == @v2_plan_id
@ -128,7 +149,9 @@ defmodule Plausible.Billing.PlansTest do
test "available_plans returns all plans without prices by default" do
user = new_user() |> subscribe_to_plan(@v2_plan_id)
assert %{growth: [_ | _], business: [_ | _]} = Plans.available_plans_for(user.subscription)
subscription = team_of(user, with_subscription?: true).subscription
assert %{growth: [_ | _], business: [_ | _]} = Plans.available_plans_for(subscription)
end
test "latest_enterprise_plan_with_price/1" do
@ -156,24 +179,30 @@ defmodule Plausible.Billing.PlansTest do
describe "subscription_interval" do
test "is based on the plan if user is on a standard plan" do
user = insert(:user, subscription: build(:subscription, paddle_plan_id: @v1_plan_id))
subscription =
new_user()
|> subscribe_to_plan(@v1_plan_id)
|> team_of(with_subscription?: true)
|> Map.fetch!(:subscription)
assert Plans.subscription_interval(user.subscription) == "monthly"
assert Plans.subscription_interval(subscription) == "monthly"
end
test "is N/A for free plan" do
user = insert(:user, subscription: build(:subscription, paddle_plan_id: "free_10k"))
subscription =
new_user()
|> subscribe_to_plan("free_10k")
|> team_of(with_subscription?: true)
|> Map.fetch!(:subscription)
assert Plans.subscription_interval(user.subscription) == "N/A"
assert Plans.subscription_interval(subscription) == "N/A"
end
test "is based on the enterprise plan if user is on an enterprise plan" do
user = insert(:user)
user = new_user()
subscribe_to_enterprise_plan(user, billing_interval: :yearly)
enterprise_plan = insert(:enterprise_plan, user_id: user.id, billing_interval: :yearly)
subscription =
insert(:subscription, user_id: user.id, paddle_plan_id: enterprise_plan.paddle_plan_id)
subscription = user |> team_of(with_subscription?: true) |> Map.fetch!(:subscription)
assert Plans.subscription_interval(subscription) == :yearly
end

View File

@ -217,20 +217,27 @@ defmodule Plausible.Billing.QuotaTest do
end
test "is based on the enterprise plan if user is on an enterprise plan" do
user = insert(:user)
enterprise_plan =
insert(:enterprise_plan, user_id: user.id, monthly_pageview_limit: 100_000)
user = new_user()
subscription =
insert(:subscription, user_id: user.id, paddle_plan_id: enterprise_plan.paddle_plan_id)
user
|> subscribe_to_enterprise_plan(monthly_pageview_limit: 100_000)
|> team_of()
|> Repo.preload(:subscription)
|> Map.fetch!(:subscription)
assert Quota.Limits.monthly_pageview_limit(subscription) == 100_000
end
test "does not limit pageviews when user has a pending enterprise plan" do
user = insert(:user)
subscription = insert(:subscription, user_id: user.id, paddle_plan_id: "pending-enterprise")
user = new_user()
subscription =
user
|> subscribe_to_plan("pending-enterprise")
|> team_of()
|> Repo.preload(:subscription)
|> Map.fetch!(:subscription)
assert Quota.Limits.monthly_pageview_limit(subscription) == :unlimited
end
@ -462,14 +469,11 @@ defmodule Plausible.Billing.QuotaTest do
end
test "returns the enterprise plan limit" do
user =
insert(:user,
enterprise_plan:
build(:enterprise_plan, paddle_plan_id: "123321", team_member_limit: 27),
subscription: build(:subscription, paddle_plan_id: "123321")
)
user = new_user()
subscribe_to_enterprise_plan(user, team_member_limit: 27)
team = team_of(user)
assert 27 == Quota.Limits.team_member_limit(user)
assert 27 == Plausible.Teams.Billing.team_member_limit(team)
end
test "reads from json file when the user is on a v4 plan" do
@ -512,13 +516,14 @@ defmodule Plausible.Billing.QuotaTest do
test "returns [Funnels] when user/site uses funnels" do
user = new_user()
site = new_site(owner: user)
team = team_of(user)
goals = insert_list(3, :goal, site: site, event_name: fn -> Ecto.UUID.generate() end)
steps = Enum.map(goals, &%{"goal_id" => &1.id})
Plausible.Funnels.create(site, "dummy", steps)
assert [Funnels] == Quota.Usage.features_usage(nil, [site.id])
assert [Funnels] == Quota.Usage.features_usage(user)
assert [Funnels] == Plausible.Teams.Billing.features_usage(nil, [site.id])
assert [Funnels] == Plausible.Teams.Billing.features_usage(team)
end
test "returns [RevenueGoals] when user/site uses revenue goals" do
@ -559,25 +564,21 @@ defmodule Plausible.Billing.QuotaTest do
owner: user
)
team = team_of(user)
insert(:goal, currency: :USD, site: site, event_name: "Purchase")
goals = insert_list(3, :goal, site: site, event_name: fn -> Ecto.UUID.generate() end)
steps = Enum.map(goals, &%{"goal_id" => &1.id})
Plausible.Funnels.create(site, "dummy", steps)
assert [Props, Funnels, RevenueGoals, StatsAPI] == Quota.Usage.features_usage(user)
assert [Props, Funnels, RevenueGoals, StatsAPI] ==
Plausible.Teams.Billing.features_usage(team)
end
end
test "accounts only for sites the user owns" do
user = insert(:user)
insert(:site,
allowed_event_props: ["dummy"],
memberships: [build(:site_membership, user: user, role: :admin)]
)
assert [] == Quota.Usage.features_usage(user)
assert [] == Plausible.Teams.Billing.features_usage(nil)
end
end
@ -606,21 +607,18 @@ defmodule Plausible.Billing.QuotaTest do
on_ee do
test "returns the enterprise plan features" do
user = insert(:user)
user = new_user()
enterprise_plan =
insert(:enterprise_plan,
user_id: user.id,
monthly_pageview_limit: 100_000,
site_limit: 500,
features: [Plausible.Billing.Feature.StatsAPI, Plausible.Billing.Feature.Funnels]
)
subscribe_to_enterprise_plan(user,
monthly_pageview_limit: 100_000,
site_limit: 500,
features: [Plausible.Billing.Feature.StatsAPI, Plausible.Billing.Feature.Funnels]
)
_subscription =
insert(:subscription, user_id: user.id, paddle_plan_id: enterprise_plan.paddle_plan_id)
team = team_of(user)
assert [Plausible.Billing.Feature.StatsAPI, Plausible.Billing.Feature.Funnels] ==
Quota.Limits.allowed_features_for(user)
Plausible.Teams.Billing.allowed_features_for(team)
end
end
@ -651,18 +649,22 @@ defmodule Plausible.Billing.QuotaTest do
end
test "returns old plan features for enterprise customers who are due to change a plan" do
user =
insert(:user,
enterprise_plan:
build(:enterprise_plan,
paddle_plan_id: "old-paddle-plan-id",
features: [Plausible.Billing.Feature.StatsAPI]
),
subscription: build(:subscription, paddle_plan_id: "old-paddle-plan-id")
)
user = new_user()
insert(:enterprise_plan, user_id: user.id, paddle_plan_id: "new-paddle-plan-id")
assert [Plausible.Billing.Feature.StatsAPI] == Quota.Limits.allowed_features_for(user)
subscribe_to_enterprise_plan(user,
paddle_plan_id: "old-paddle-plan-id",
features: [Plausible.Billing.Feature.StatsAPI]
)
subscribe_to_enterprise_plan(user,
paddle_plan_id: "new-paddle-plan-id",
subscription?: false
)
team = team_of(user)
assert [Plausible.Billing.Feature.StatsAPI] ==
Plausible.Teams.Billing.allowed_features_for(team)
end
end

View File

@ -1,5 +1,6 @@
defmodule Plausible.HelpScoutTest do
use Plausible.DataCase, async: true
use Plausible.Teams.Test
use Plausible
@moduletag :ee_only
@ -54,7 +55,7 @@ defmodule Plausible.HelpScoutTest do
describe "get_details_for_customer/2" do
test "returns details for user on trial" do
%{id: user_id, email: email} = insert(:user)
%{id: user_id, email: email} = new_user()
stub_help_scout_requests(email)
crm_url = "#{PlausibleWeb.Endpoint.url()}/crm/auth/user/#{user_id}"
@ -74,7 +75,7 @@ defmodule Plausible.HelpScoutTest do
end
test "returns for user without trial or subscription" do
%{email: email} = insert(:user, trial_expiry_date: nil)
%{email: email} = new_user(trial_expiry_date: nil)
stub_help_scout_requests(email)
assert {:ok,
@ -87,7 +88,9 @@ defmodule Plausible.HelpScoutTest do
end
test "returns for user with trial expired" do
%{email: email} = insert(:user, trial_expiry_date: Date.add(Date.utc_today(), -1))
%{email: email} =
new_user(trial_expiry_date: Date.add(Date.utc_today(), -1))
stub_help_scout_requests(email)
assert {:ok,
@ -100,10 +103,15 @@ defmodule Plausible.HelpScoutTest do
end
test "returns for user with paid subscription on standard plan" do
user = %{email: email} = insert(:user, trial_expiry_date: Date.add(Date.utc_today(), -1))
user = %{email: email} = new_user(trial_expiry_date: Date.add(Date.utc_today(), -1))
%{paddle_subscription_id: paddle_subscription_id} =
insert(:subscription, user: user, paddle_plan_id: @v4_business_monthly_plan_id)
subscribe_to_plan(user, @v4_business_monthly_plan_id)
paddle_subscription_id =
user
|> team_of(with_subscription?: true)
|> Map.fetch!(:subscription)
|> Map.fetch!(:paddle_subscription_id)
stub_help_scout_requests(email)
@ -120,10 +128,15 @@ defmodule Plausible.HelpScoutTest do
end
test "returns for user with paid subscription on standard yearly plan" do
user = %{email: email} = insert(:user, trial_expiry_date: Date.add(Date.utc_today(), -1))
user = %{email: email} = new_user(trial_expiry_date: Date.add(Date.utc_today(), -1))
%{paddle_subscription_id: paddle_subscription_id} =
insert(:subscription, user: user, paddle_plan_id: @v4_business_yearly_plan_id)
subscribe_to_plan(user, @v4_business_yearly_plan_id)
paddle_subscription_id =
user
|> team_of(with_subscription?: true)
|> Map.fetch!(:subscription)
|> Map.fetch!(:paddle_subscription_id)
stub_help_scout_requests(email)
@ -140,9 +153,9 @@ defmodule Plausible.HelpScoutTest do
end
test "returns for user with paid subscription on free 10k plan" do
user = %{email: email} = insert(:user, trial_expiry_date: Date.add(Date.utc_today(), -1))
user = %{email: email} = new_user(trial_expiry_date: Date.add(Date.utc_today(), -1))
insert(:subscription, user: user, paddle_plan_id: "free_10k")
subscribe_to_plan(user, "free_10k")
stub_help_scout_requests(email)
@ -156,15 +169,11 @@ defmodule Plausible.HelpScoutTest do
end
test "returns for user with paid subscription on enterprise plan" do
user = %{email: email} = insert(:user, trial_expiry_date: Date.add(Date.utc_today(), -1))
user = %{email: email} = new_user(trial_expiry_date: Date.add(Date.utc_today(), -1))
ep =
insert(:enterprise_plan,
features: [Plausible.Billing.Feature.StatsAPI],
user_id: user.id
)
insert(:subscription, user: user, paddle_plan_id: ep.paddle_plan_id)
subscribe_to_enterprise_plan(user,
features: [Plausible.Billing.Feature.StatsAPI]
)
stub_help_scout_requests(email)
@ -178,16 +187,12 @@ defmodule Plausible.HelpScoutTest do
end
test "returns for user with paid subscription on yearly enterprise plan" do
user = %{email: email} = insert(:user, trial_expiry_date: Date.add(Date.utc_today(), -1))
user = %{email: email} = new_user(trial_expiry_date: Date.add(Date.utc_today(), -1))
ep =
insert(:enterprise_plan,
features: [Plausible.Billing.Feature.StatsAPI],
user_id: user.id,
billing_interval: :yearly
)
insert(:subscription, user: user, paddle_plan_id: ep.paddle_plan_id)
subscribe_to_enterprise_plan(user,
features: [Plausible.Billing.Feature.StatsAPI],
billing_interval: :yearly
)
stub_help_scout_requests(email)
@ -201,12 +206,10 @@ defmodule Plausible.HelpScoutTest do
end
test "returns for user with subscription pending cancellation" do
user = %{email: email} = insert(:user, trial_expiry_date: Date.add(Date.utc_today(), -1))
user = %{email: email} = new_user(trial_expiry_date: Date.add(Date.utc_today(), -1))
insert(:subscription,
user: user,
status: Subscription.Status.deleted(),
paddle_plan_id: @v4_business_monthly_plan_id
subscribe_to_plan(user, @v4_business_monthly_plan_id,
status: Subscription.Status.deleted()
)
stub_help_scout_requests(email)
@ -221,12 +224,10 @@ defmodule Plausible.HelpScoutTest do
end
test "returns for user with canceled subscription" do
user = %{email: email} = insert(:user, trial_expiry_date: Date.add(Date.utc_today(), -1))
user = %{email: email} = new_user(trial_expiry_date: Date.add(Date.utc_today(), -1))
insert(:subscription,
user: user,
subscribe_to_plan(user, @v4_business_monthly_plan_id,
status: Subscription.Status.deleted(),
paddle_plan_id: @v4_business_monthly_plan_id,
next_bill_date: Date.add(Date.utc_today(), -1)
)
@ -242,12 +243,10 @@ defmodule Plausible.HelpScoutTest do
end
test "returns for user with paused subscription" do
user = %{email: email} = insert(:user, trial_expiry_date: Date.add(Date.utc_today(), -1))
user = %{email: email} = new_user(trial_expiry_date: Date.add(Date.utc_today(), -1))
insert(:subscription,
user: user,
status: Subscription.Status.paused(),
paddle_plan_id: @v4_business_monthly_plan_id
subscribe_to_plan(user, @v4_business_monthly_plan_id,
status: Subscription.Status.paused()
)
stub_help_scout_requests(email)
@ -262,11 +261,10 @@ defmodule Plausible.HelpScoutTest do
end
test "returns for user with locked site" do
user = %{email: email} = insert(:user, trial_expiry_date: Date.add(Date.utc_today(), -1))
user = %{email: email} = new_user(trial_expiry_date: Date.add(Date.utc_today(), -1))
insert(:site, members: [user], locked: true)
insert(:subscription, user: user, paddle_plan_id: @v4_business_monthly_plan_id)
new_site(owner: user, locked: true)
subscribe_to_plan(user, @v4_business_monthly_plan_id)
stub_help_scout_requests(email)
@ -281,7 +279,7 @@ defmodule Plausible.HelpScoutTest do
end
test "returns error when no matching user found in database" do
insert(:user)
new_user()
stub_help_scout_requests("another@example.com")
@ -329,7 +327,7 @@ defmodule Plausible.HelpScoutTest do
end
test "uses existing access token when available" do
%{email: email} = insert(:user)
%{email: email} = new_user()
now = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second)
Repo.insert_all("help_scout_credentials", [
@ -359,7 +357,7 @@ defmodule Plausible.HelpScoutTest do
end
test "refreshes token on expiry" do
%{email: email} = insert(:user)
%{email: email} = new_user()
now = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second)
Repo.insert_all("help_scout_credentials", [
@ -407,7 +405,7 @@ defmodule Plausible.HelpScoutTest do
describe "get_details_for_emails/2" do
test "returns details for user and persists mapping" do
%{id: user_id, email: email} = insert(:user)
%{id: user_id, email: email} = new_user()
crm_url = "#{PlausibleWeb.Endpoint.url()}/crm/auth/user/#{user_id}"
@ -428,8 +426,9 @@ defmodule Plausible.HelpScoutTest do
end
test "updates mapping if one already exists" do
user = insert(:user)
%{email: new_email} = insert(:user)
user = new_user()
%{email: new_email} = new_user()
HelpScout.set_mapping("123", user.email)
assert {:ok, _} = HelpScout.get_details_for_emails([new_email], "123")
@ -437,12 +436,14 @@ defmodule Plausible.HelpScoutTest do
end
test "picks the match with largest number of owned sites" do
user1 = insert(:user)
insert(:site, members: [user1])
insert(:site_membership, site: build(:site), user: user1, role: :viewer)
insert(:site_membership, site: build(:site), user: user1, role: :admin)
user2 = insert(:user)
insert_list(2, :site, members: [user2])
user1 = new_user()
new_site(owner: user1)
add_guest(new_site(), user: user1, role: :viewer)
add_guest(new_site(), user: user1, role: :editor)
user2 = new_user()
new_site(owner: user2)
new_site(owner: user2)
crm_url = "#{PlausibleWeb.Endpoint.url()}/crm/auth/user/#{user2.id}"
@ -473,14 +474,14 @@ defmodule Plausible.HelpScoutTest do
describe "search_users/2" do
test "lists matching users by email or site domain ordered by site counts" do
user1 = insert(:user, email: "user1@match.example.com")
user1 = new_user(email: "user1@match.example.com")
user2 = insert(:user, email: "user2@match.example.com")
insert(:site, members: [user2])
user2 = new_user(email: "user2@match.example.com")
new_site(owner: user2)
user3 = insert(:user, email: "user3@umatched.example.com")
insert(:site, members: [user3])
insert(:site, domain: "big.match.example.com/hit", members: [user3])
user3 = new_user(email: "user3@umatched.example.com")
new_site(owner: user3)
new_site(domain: "big.match.example.com/hit", owner: user3)
assert HelpScout.search_users("match.example.co", "123") == [
%{email: user3.email, sites_count: 2},

View File

@ -2,6 +2,7 @@ defmodule Plausible.Imported.CSVImporterTest do
use Plausible
use Plausible.Repo
use PlausibleWeb.ConnCase
use Plausible.Teams.Test
use Bamboo.Test
alias Plausible.Imported.{CSVImporter, SiteImport}
require SiteImport
@ -486,8 +487,8 @@ defmodule Plausible.Imported.CSVImporterTest do
@tag :tmp_dir
test "it works", %{conn: conn, user: user, tmp_dir: tmp_dir} do
exported_site = insert(:site, members: [user])
imported_site = insert(:site, members: [user])
exported_site = new_site(owner: user)
imported_site = new_site(owner: user)
insert(:goal, site: exported_site, event_name: "Outbound Link: Click")
insert(:goal, site: exported_site, event_name: "404")

View File

@ -1,5 +1,6 @@
defmodule Plausible.Ingestion.CountersTest do
use Plausible.DataCase, async: false
use Plausible.Teams.Test
import Ecto.Query
alias Plausible.Ingestion.Counters
@ -115,7 +116,7 @@ defmodule Plausible.Ingestion.CountersTest do
domain = Keyword.get(opts, :domain, random_domain())
at = Keyword.get(opts, :at, NaiveDateTime.utc_now())
site = insert(:site, domain: domain)
site = new_site(domain: domain)
payload = %{
name: "pageview",

View File

@ -1,5 +1,6 @@
defmodule Plausible.Ingestion.EventTelemetryTest do
import Phoenix.ConnTest
import Plausible.Teams.Test
alias Plausible.Ingestion.Request
alias Plausible.Ingestion.Event
@ -24,7 +25,7 @@ defmodule Plausible.Ingestion.EventTelemetryTest do
%{}
)
site = insert(:site, ingest_rate_limit_threshold: 2)
site = new_site(ingest_rate_limit_threshold: 2)
payload = %{
name: "pageview",

View File

@ -1,5 +1,6 @@
defmodule Plausible.Ingestion.EventTest do
use Plausible.DataCase, async: true
use Plausible.Teams.Test
import Phoenix.ConnTest
@ -7,7 +8,7 @@ defmodule Plausible.Ingestion.EventTest do
alias Plausible.Ingestion.Event
test "processes a request into an event" do
site = insert(:site)
site = new_site()
payload = %{
name: "pageview",
@ -27,7 +28,7 @@ defmodule Plausible.Ingestion.EventTest do
for {user_agent, idx} <- Enum.with_index(@regressive_user_agents) do
test "processes user agents known to cause problems parsing in the past (case #{idx})" do
site = insert(:site)
site = new_site()
payload = %{
name: "pageview",
@ -45,7 +46,7 @@ defmodule Plausible.Ingestion.EventTest do
end
test "drops verification agent" do
site = insert(:site)
site = new_site()
payload = %{
name: "pageview",
@ -76,7 +77,7 @@ defmodule Plausible.Ingestion.EventTest do
end
test "drops a request when referrer is spam" do
site = insert(:site)
site = new_site()
payload = %{
name: "pageview",
@ -93,7 +94,7 @@ defmodule Plausible.Ingestion.EventTest do
end
test "drops a request when referrer is spam for multiple domains" do
site = insert(:site)
site = new_site()
payload = %{
name: "pageview",
@ -110,7 +111,7 @@ defmodule Plausible.Ingestion.EventTest do
end
test "selectively drops an event for multiple domains" do
site = insert(:site)
site = new_site()
payload = %{
name: "pageview",
@ -126,7 +127,7 @@ defmodule Plausible.Ingestion.EventTest do
end
test "selectively drops an event when rate-limited" do
site = insert(:site, ingest_rate_limit_threshold: 1)
site = new_site(ingest_rate_limit_threshold: 1)
payload = %{
name: "pageview",
@ -143,7 +144,7 @@ defmodule Plausible.Ingestion.EventTest do
end
test "drops a request when header x-plausible-ip-type is dc_ip" do
site = insert(:site)
site = new_site()
payload = %{
name: "pageview",
@ -160,7 +161,7 @@ defmodule Plausible.Ingestion.EventTest do
end
test "drops a request when ip is on blocklist" do
site = insert(:site)
site = new_site()
payload = %{
name: "pageview",
@ -180,7 +181,7 @@ defmodule Plausible.Ingestion.EventTest do
end
test "drops a request when country is on blocklist" do
site = insert(:site)
site = new_site()
payload = %{
name: "pageview",
@ -201,7 +202,7 @@ defmodule Plausible.Ingestion.EventTest do
end
test "drops a request when page is on blocklist" do
site = insert(:site)
site = new_site()
payload = %{
name: "pageview",
@ -220,7 +221,7 @@ defmodule Plausible.Ingestion.EventTest do
end
test "drops a request when hostname allowlist is defined and hostname is not on the list" do
site = insert(:site)
site = new_site()
payload = %{
name: "pageview",
@ -239,7 +240,7 @@ defmodule Plausible.Ingestion.EventTest do
end
test "passes a request when hostname allowlist is defined and hostname is on the list" do
site = insert(:site)
site = new_site()
payload = %{
name: "pageview",
@ -259,11 +260,8 @@ defmodule Plausible.Ingestion.EventTest do
test "drops events for site with accept_trafic_until in the past" do
yesterday = Date.add(Date.utc_today(), -1)
site =
insert(:site,
ingest_rate_limit_threshold: 1,
members: [build(:user, accept_traffic_until: yesterday)]
)
owner = new_user(team: [accept_traffic_until: yesterday])
site = new_site(ingest_rate_limit_threshold: 1, owner: owner)
payload = %{
name: "pageview",
@ -280,7 +278,7 @@ defmodule Plausible.Ingestion.EventTest do
@tag :slow
test "drops events on session lock timeout" do
site = insert(:site)
site = new_site()
very_slow_buffer = fn sessions ->
Process.sleep(1000)
@ -319,7 +317,7 @@ defmodule Plausible.Ingestion.EventTest do
end
test "drops pageleave event when no session found from cache" do
site = insert(:site)
site = new_site()
payload = %{
name: "pageleave",
@ -336,7 +334,7 @@ defmodule Plausible.Ingestion.EventTest do
@tag :ee_only
test "saves revenue amount" do
site = insert(:site)
site = new_site()
_goal = insert(:goal, event_name: "checkout", currency: "USD", site: site)
payload = %{
@ -353,7 +351,7 @@ defmodule Plausible.Ingestion.EventTest do
end
test "does not save revenue amount when there is no revenue goal" do
site = insert(:site)
site = new_site()
payload = %{
name: "checkout",
@ -369,7 +367,7 @@ defmodule Plausible.Ingestion.EventTest do
end
test "IPv4 hostname is stored without public suffix processing" do
_site = insert(:site, domain: "192.168.0.1")
_site = new_site(domain: "192.168.0.1")
payload = %{
name: "checkout",
@ -384,7 +382,7 @@ defmodule Plausible.Ingestion.EventTest do
end
test "Hostname is stored with public suffix processing" do
_site = insert(:site, domain: "foo.netlify.app")
_site = new_site(domain: "foo.netlify.app")
payload = %{
name: "checkout",
@ -399,7 +397,7 @@ defmodule Plausible.Ingestion.EventTest do
end
test "hostname is (none) when no hostname can be derived from the url" do
site = insert(:site, domain: "foo.example.com")
site = new_site(domain: "foo.example.com")
payload = %{
domain: site.domain,

View File

@ -13,26 +13,15 @@ defmodule Plausible.Site.CacheTest do
name: :"cache_supervisor_#{test}"
)
%{id: first_id} = site1 = insert(:site, domain: "site1.example.com")
%{id: first_id} = site1 = new_site(domain: "site1.example.com")
_ =
insert(:site,
domain: "site2.example.com",
memberships: [
build(:site_membership,
user: build(:user, accept_traffic_until: ~D[2022-01-01]),
role: :viewer
),
build(:site_membership,
user: build(:user, accept_traffic_until: ~D[2021-01-01]),
role: :owner
),
build(:site_membership,
user: build(:user, accept_traffic_until: ~D[2020-01-01]),
role: :admin
)
]
)
owner = new_user(team: [accept_traffic_until: ~D[2021-01-01]])
viewer = new_user(team: [accept_traffic_until: ~D[2022-01-01]])
admin = new_user(team: [accept_traffic_until: ~D[2020-01-01]])
site = new_site(owner: owner, domain: "site2.example.com")
add_guest(site, user: viewer, role: :viewer)
add_guest(site, user: admin, role: :editor)
:ok = Cache.refresh_all(cache_name: test)
@ -46,7 +35,7 @@ defmodule Plausible.Site.CacheTest do
assert %Site{from_cache?: true} =
Cache.get("site2.example.com", force?: true, cache_name: test)
assert %Site{from_cache?: false, owner: %{accept_traffic_until: ~D[2021-01-01]}} =
assert %Site{from_cache?: false, team: %{accept_traffic_until: ~D[2021-01-01]}} =
Cache.get("site2.example.com", cache_name: test)
refute Cache.get("site3.example.com", cache_name: test, force?: true)
@ -143,14 +132,14 @@ defmodule Plausible.Site.CacheTest do
test "cache is ready when refreshed", %{test: test} do
{:ok, _} = start_test_cache(test)
insert(:site)
new_site()
:ok = Cache.refresh_all(cache_name: test)
assert Cache.ready?(test)
end
test "cache allows lookups for sites with changed domain", %{test: test} do
{:ok, _} = start_test_cache(test)
insert(:site, domain: "new.example.com", domain_changed_from: "old.example.com")
new_site(domain: "new.example.com", domain_changed_from: "old.example.com")
:ok = Cache.refresh_all(cache_name: test)
assert Cache.get("old.example.com", force?: true, cache_name: test)
@ -160,7 +149,7 @@ defmodule Plausible.Site.CacheTest do
test "cache exposes hit rate", %{test: test} do
{:ok, _} = start_test_cache(test)
insert(:site, domain: "site1.example.com")
new_site(domain: "site1.example.com")
:ok = Cache.refresh_all(cache_name: test)
assert Cache.hit_rate(test) == 0
@ -179,9 +168,9 @@ defmodule Plausible.Site.CacheTest do
cache_opts = [cache_name: test, force?: true]
yesterday = DateTime.utc_now() |> DateTime.add(-1 * 60 * 60 * 24)
insert(:site, domain: domain1, inserted_at: yesterday, updated_at: yesterday)
new_site(domain: domain1, inserted_at: yesterday, updated_at: yesterday)
insert(:site, domain: domain2)
new_site(domain: domain2)
assert Cache.get(domain1, cache_opts) == nil
assert Cache.get(domain2, cache_opts) == nil
@ -199,7 +188,7 @@ defmodule Plausible.Site.CacheTest do
domain1 = "first.example.com"
domain2 = "second.example.com"
site = insert(:site, domain: domain1)
site = new_site(domain: domain1)
assert :ok = Cache.refresh_updated_recently(cache_opts)
assert item = Cache.get(domain1, cache_opts)
refute item.domain_changed_from
@ -250,7 +239,7 @@ defmodule Plausible.Site.CacheTest do
test "get_site_id/2", %{test: test} do
{:ok, _} = start_test_cache(test)
site = insert(:site)
site = new_site()
domain1 = site.domain
domain2 = "nonexisting.example.com"
@ -353,8 +342,8 @@ defmodule Plausible.Site.CacheTest do
domain1 = "site1.example.com"
domain2 = "site2.example.com"
site1 = insert(:site, domain: domain1)
_site2 = insert(:site, domain: domain2)
site1 = new_site(domain: domain1)
_site2 = new_site(domain: domain2)
cache_opts = [cache_name: test, force?: true]

View File

@ -1,5 +1,6 @@
defmodule Plausible.Site.GateKeeperTest do
use Plausible.DataCase, async: true
use Plausible.Teams.Test
alias Plausible.Site.Cache
alias Plausible.Site.GateKeeper
@ -18,10 +19,12 @@ defmodule Plausible.Site.GateKeeperTest do
domain = "expired.example.com"
yesterday = Date.utc_today() |> Date.add(-1)
owner = new_user(team: [accept_traffic_until: yesterday])
%{id: _} =
add_site_and_refresh_cache(test,
domain: domain,
members: [build(:user, accept_traffic_until: yesterday)]
owner: owner
)
assert {:deny, :payment_required} = GateKeeper.check(domain, opts)
@ -88,7 +91,7 @@ defmodule Plausible.Site.GateKeeperTest do
{:ok, _} = Plausible.Repo.delete(site)
# We need some dummy site, otherwise the cache won't refresh in case the DB
# is completely empty
insert(:site)
new_site()
deleted_site_id = site.id
assert {:allow, %Plausible.Site{id: ^deleted_site_id, from_cache?: true}} =
@ -104,7 +107,7 @@ defmodule Plausible.Site.GateKeeperTest do
end
defp add_site_and_refresh_cache(cache_name, site_data) do
site = insert(:site, site_data)
site = new_site(site_data)
Cache.refresh_updated_recently(cache_name: cache_name, force?: true)
site

View File

@ -108,25 +108,16 @@ defmodule Plausible.Site.Memberships.AcceptInvitationTest do
describe "accept_invitation/3 - invitations" do
test "converts an invitation into a membership" do
inviter = insert(:user)
invitee = insert(:user)
site = insert(:site, members: [inviter])
inviter = new_user()
invitee = new_user()
site = new_site(owner: inviter)
invitation =
insert(:invitation,
site_id: site.id,
inviter: inviter,
email: invitee.email,
role: :admin
)
invitation = invite_guest(site, invitee, inviter: inviter, role: :editor)
assert {:ok, membership} =
assert {:ok, _} =
AcceptInvitation.accept_invitation(invitation.invitation_id, invitee)
assert membership.site_id == site.id
assert membership.user_id == invitee.id
assert membership.role == :admin
refute Repo.reload(invitation)
assert_team_membership(invitee, site.team, :editor)
assert_email_delivered_with(
to: [nil: inviter.email],
@ -157,71 +148,26 @@ defmodule Plausible.Site.Memberships.AcceptInvitationTest do
assert team_membership.guest_memberships == []
end
@tag :teams
test "sync newly converted membership with team" do
inviter = insert(:user)
invitee = insert(:user)
site = insert(:site, members: [inviter])
invitation =
insert(:invitation,
site_id: site.id,
inviter: inviter,
email: invitee.email,
role: :admin
)
assert {:ok, membership} =
AcceptInvitation.accept_invitation(invitation.invitation_id, invitee)
assert membership.site_id == site.id
assert membership.user_id == invitee.id
assert membership.role == :admin
team = assert_team_exists(inviter)
assert_team_attached(site, team.id)
assert_guest_membership(team, site, invitee, :editor)
end
test "does not degrade role when trying to invite self as an owner" do
user = insert(:user)
user = new_user()
site = new_site(owner: user)
%{memberships: [membership]} =
site = insert(:site, memberships: [build(:site_membership, user: user, role: :owner)])
invitation = invite_guest(site, user, inviter: user, role: :editor)
invitation =
insert(:invitation,
site_id: site.id,
inviter: user,
email: user.email,
role: :admin
)
assert {:ok, invited_membership} =
assert {:ok, _} =
AcceptInvitation.accept_invitation(invitation.invitation_id, user)
assert invited_membership.id == membership.id
membership = Repo.reload!(membership)
assert membership.role == :owner
assert membership.site_id == site.id
assert membership.user_id == user.id
refute Repo.reload(invitation)
assert_team_membership(user, site.team, :owner)
end
test "handles accepting invitation as already a member gracefully" do
inviter = insert(:user)
invitee = insert(:user)
site = insert(:site, members: [inviter])
existing_membership = insert(:site_membership, user: invitee, site: site, role: :admin)
inviter = new_user()
invitee = new_user()
site = new_site(owner: inviter)
user = add_guest(site, user: invitee, role: :editor)
existing_membership = List.first(user.site_memberships)
invitation =
insert(:invitation,
site_id: site.id,
inviter: inviter,
email: invitee.email,
role: :viewer
)
invitation = invite_guest(site, invitee, inviter: inviter, role: :viewer)
assert {:ok, new_membership} =
AcceptInvitation.accept_invitation(invitation.invitation_id, invitee)
@ -425,10 +371,10 @@ defmodule Plausible.Site.Memberships.AcceptInvitationTest do
generate_usage_for(old_owner_site, 6_000, somewhere_last_month)
generate_usage_for(old_owner_site, 10_000, somewhere_penultimate_month)
invitation = invite_transfer(old_owner_site, new_owner, inviter: old_owner)
transfer_id = invite_transfer(old_owner_site, new_owner, inviter: old_owner).invitation_id
assert {:error, {:over_plan_limits, [:monthly_pageview_limit]}} =
AcceptInvitation.accept_invitation(invitation.invitation_id, new_owner)
AcceptInvitation.accept_invitation(transfer_id, new_owner)
end
@tag :ee_only

View File

@ -1,5 +1,6 @@
defmodule Plausible.Site.Memberships.RejectInvitationTest do
use Plausible
use Plausible.Teams.Test
use Plausible.DataCase, async: true
use Bamboo.Test
@ -8,17 +9,11 @@ defmodule Plausible.Site.Memberships.RejectInvitationTest do
alias Plausible.Site.Memberships.RejectInvitation
test "rejects invitation and sends email to inviter" do
inviter = insert(:user)
invitee = insert(:user)
site = insert(:site, members: [inviter])
inviter = new_user()
invitee = new_user()
site = new_site(owner: inviter)
invitation =
insert(:invitation,
site_id: site.id,
inviter: inviter,
email: invitee.email,
role: :admin
)
invitation = invite_guest(site, invitee, inviter: inviter, role: :editor)
assert {:ok, rejected_invitation} =
RejectInvitation.reject_invitation(invitation.invitation_id, invitee)

View File

@ -1,20 +1,16 @@
defmodule Plausible.Site.Memberships.RemoveInvitationTest do
use Plausible.DataCase, async: true
use Plausible.Teams.Test
alias Plausible.Site.Memberships.RemoveInvitation
test "removes invitation" do
inviter = insert(:user)
invitee = insert(:user)
site = insert(:site, members: [inviter])
inviter = new_user()
invitee = new_user()
site = new_site(owner: inviter)
invitation =
insert(:invitation,
site_id: site.id,
inviter: inviter,
email: invitee.email,
role: :admin
)
invite_guest(site, invitee, inviter: inviter, role: :editor)
assert {:ok, removed_invitation} =
RemoveInvitation.remove_invitation(invitation.invitation_id, site)

View File

@ -1,12 +1,13 @@
defmodule Plausible.Site.SiteRemovalTest do
use Plausible.DataCase, async: true
use Oban.Testing, repo: Plausible.Repo
use Plausible.Teams.Test
alias Plausible.Site.Removal
alias Plausible.Sites
test "site from postgres is immediately deleted" do
site = insert(:site)
site = new_site()
assert {:ok, context} = Removal.run(site)
assert context.delete_all == {1, nil}
refute Sites.get_by_domain(site.domain)
@ -14,7 +15,8 @@ defmodule Plausible.Site.SiteRemovalTest do
@tag :teams
test "site deletion prunes team guest memberships" do
site = insert(:site) |> Plausible.Teams.load_for_site() |> Repo.preload(:owner)
owner = new_user()
site = new_site(owner: owner)
team_membership =
insert(:team_membership, user: build(:user), team: site.team, role: :guest)
@ -25,7 +27,7 @@ defmodule Plausible.Site.SiteRemovalTest do
insert(:team_invitation,
email: "sitedeletion@example.test",
team: site.team,
inviter: site.owner,
inviter: owner,
role: :guest
)

View File

@ -212,6 +212,40 @@ defmodule Plausible.SitesTest do
} = Plausible.Teams.Sites.list_with_invitations(user, %{})
end
test "prioritizes pending transfer over pinned site with guest membership" do
owner = new_user()
pending_owner = new_user()
site = new_site(owner: owner, domain: "one.example.com")
add_guest(site, user: pending_owner, role: :editor)
invite_transfer(site, pending_owner, inviter: owner)
{:ok, _} = Sites.toggle_pin(pending_owner, site)
assert %{
entries: [
%{domain: "one.example.com", entry_type: "invitation"}
]
} =
Sites.list_with_invitations(pending_owner, %{})
end
test "prioritizes pending transfer over site with guest membership" do
owner = new_user()
pending_owner = new_user()
site = new_site(owner: owner, domain: "one.example.com")
add_guest(site, user: pending_owner, role: :editor)
invite_transfer(site, pending_owner, inviter: owner)
assert %{
entries: [
%{domain: "one.example.com", entry_type: "invitation"}
]
} =
Sites.list_with_invitations(pending_owner, %{})
end
test "pinned site doesn't matter with membership revoked (no active invitations)" do
user1 = new_user(email: "user1@example.com")
_user2 = new_user(email: "user2@example.com")
@ -570,6 +604,20 @@ defmodule Plausible.SitesTest do
assert prefs.pinned_at
end
test "handles multiple guest memberships with same team properly (regression)" do
user = new_user()
owner = new_user()
site1 = new_site(owner: owner)
site2 = new_site(owner: owner)
add_guest(site1, user: user, role: :viewer)
add_guest(site2, user: user, role: :viewer)
assert {:ok, prefs} = Sites.toggle_pin(user, site1)
assert prefs.site_id == site1.id
assert prefs.user_id == user.id
assert prefs.pinned_at
end
test "returns error when pins limit hit" do
user = new_user()

View File

@ -1545,15 +1545,8 @@ defmodule Plausible.Stats.Filters.QueryParserTest do
@describetag :ee_only
setup %{user: user} do
plan =
insert(:enterprise_plan,
features: [Plausible.Billing.Feature.RevenueGoals],
user_id: user.id
)
subscription = insert(:subscription, user: user, paddle_plan_id: plan.paddle_plan_id)
{:ok, subscription: subscription}
subscribe_to_enterprise_plan(user, features: [Plausible.Billing.Feature.RevenueGoals])
:ok
end
test "not valid in public schema", %{site: site} do
@ -1590,8 +1583,9 @@ defmodule Plausible.Stats.Filters.QueryParserTest do
)
end
test "no access", %{site: site, user: user, subscription: subscription} do
Repo.delete!(subscription)
test "no access" do
user = new_user()
site = new_site(owner: user)
subscribe_to_enterprise_plan(user, features: [Plausible.Billing.Feature.StatsAPI])

View File

@ -1,13 +1,14 @@
defmodule Plausible.Stats.QueryResultTest do
use Plausible.DataCase, async: true
use Plausible.Teams.Test
alias Plausible.Stats.{Query, QueryResult, QueryOptimizer}
setup do
user = insert(:user)
site =
insert(:site,
members: [user],
new_site(
owner: user,
inserted_at: ~N[2020-01-01T00:00:00],
stats_start_date: ~D[2020-01-01]
)
@ -15,9 +16,7 @@ defmodule Plausible.Stats.QueryResultTest do
{:ok, site: site}
end
test "serializing query to JSON keeps keys ordered" do
site = insert(:site)
test "serializing query to JSON keeps keys ordered", %{site: site} do
{:ok, query} =
Query.build(
site,

View File

@ -10,17 +10,20 @@ defmodule PlausibleWeb.Components.Billing.NoticeTest do
assert render_component(&Notice.premium_feature/1,
billable_user: me,
current_user: me,
current_team: team_of(me),
feature_mod: Plausible.Billing.Feature.Props
) == ""
end
test "premium_feature/1 renders an upgrade link when user is the site owner and does not have access to the feature" do
me = new_user() |> subscribe_to_growth_plan()
team = team_of(me)
rendered =
render_component(&Notice.premium_feature/1,
billable_user: me,
current_user: me,
current_team: team,
feature_mod: Plausible.Billing.Feature.Props
)
@ -37,6 +40,7 @@ defmodule PlausibleWeb.Components.Billing.NoticeTest do
render_component(&Notice.premium_feature/1,
billable_user: owner,
current_user: me,
current_team: team_of(me),
feature_mod: Plausible.Billing.Feature.Funnels
)
@ -51,6 +55,7 @@ defmodule PlausibleWeb.Components.Billing.NoticeTest do
render_component(&Notice.premium_feature/1,
billable_user: me,
current_user: me,
current_team: team_of(me),
feature_mod: Plausible.Billing.Feature.Funnels
)
@ -59,11 +64,13 @@ defmodule PlausibleWeb.Components.Billing.NoticeTest do
test "limit_exceeded/1 when billable user is on growth displays upgrade link" do
me = new_user() |> subscribe_to_growth_plan()
team = team_of(me)
rendered =
render_component(&Notice.limit_exceeded/1,
billable_user: me,
current_user: me,
current_team: team,
limit: 10,
resource: "users"
)
@ -80,6 +87,7 @@ defmodule PlausibleWeb.Components.Billing.NoticeTest do
render_component(&Notice.limit_exceeded/1,
billable_user: me,
current_user: insert(:user),
current_team: team_of(me),
limit: 10,
resource: "users"
)
@ -96,6 +104,7 @@ defmodule PlausibleWeb.Components.Billing.NoticeTest do
render_component(&Notice.limit_exceeded/1,
billable_user: me,
current_user: me,
current_team: team_of(me),
limit: 10,
resource: "users"
)
@ -113,6 +122,7 @@ defmodule PlausibleWeb.Components.Billing.NoticeTest do
render_component(&Notice.limit_exceeded/1,
billable_user: me,
current_user: me,
current_team: team_of(me),
limit: 10,
resource: "users"
)
@ -126,11 +136,13 @@ defmodule PlausibleWeb.Components.Billing.NoticeTest do
@tag :ee_only
test "limit_exceeded/1 when billable user is on a business plan displays support email" do
me = new_user() |> subscribe_to_business_plan()
team = team_of(me)
rendered =
render_component(&Notice.limit_exceeded/1,
billable_user: me,
current_user: me,
current_team: team,
limit: 10,
resource: "users"
)

View File

@ -1,5 +1,6 @@
defmodule PlausibleWeb.AdminControllerTest do
use PlausibleWeb.ConnCase, async: false
use Plausible.Teams.Test
alias Plausible.Repo
@ -37,12 +38,12 @@ defmodule PlausibleWeb.AdminControllerTest do
} do
patch_env(:super_admin_user_ids, [user.id])
s1 = insert(:site, inserted_at: ~N[2024-01-01 00:00:00])
insert_list(3, :site_membership, site: s1)
s2 = insert(:site, inserted_at: ~N[2024-01-02 00:00:00])
insert_list(3, :site_membership, site: s2)
s3 = insert(:site, inserted_at: ~N[2024-01-03 00:00:00])
insert_list(3, :site_membership, site: s3)
s1 = new_site(inserted_at: ~N[2024-01-01 00:00:00])
for _ <- 1..3, do: add_guest(s1, role: :viewer)
s2 = new_site(inserted_at: ~N[2024-01-02 00:00:00])
for _ <- 1..3, do: add_guest(s2, role: :viewer)
s3 = new_site(inserted_at: ~N[2024-01-03 00:00:00])
for _ <- 1..3, do: add_guest(s3, role: :viewer)
conn1 = get(conn, "/crm/sites/site", %{"limit" => "2"})
page1_html = html_response(conn1, 200)
@ -128,7 +129,7 @@ defmodule PlausibleWeb.AdminControllerTest do
} do
patch_env(:super_admin_user_ids, [user.id])
insert(:subscription, user: user)
subscribe_to_plan(user, "does-not-exist")
conn = get(conn, "/crm/billing/user/#{user.id}/current_plan")
assert json_response(conn, 200) == %{"features" => []}
@ -138,7 +139,7 @@ defmodule PlausibleWeb.AdminControllerTest do
test "returns plan data for user with subscription", %{conn: conn, user: user} do
patch_env(:super_admin_user_ids, [user.id])
insert(:subscription, user: user, paddle_plan_id: "857104")
subscribe_to_plan(user, "857104")
conn = get(conn, "/crm/billing/user/#{user.id}/current_plan")

View File

@ -1,6 +1,7 @@
defmodule PlausibleWeb.Api.ExternalControllerTest do
use PlausibleWeb.ConnCase
use Plausible.ClickhouseRepo
use Plausible.Teams.Test
@user_agent "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36"
@user_agent_mobile "Mozilla/5.0 (Linux; Android 6.0; U007 Pro Build/MRA58K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/44.0.2403.119 Mobile Safari/537.36"
@ -8,7 +9,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do
describe "POST /api/event" do
setup do
site = insert(:site)
site = new_site()
{:ok, site: site}
end
@ -72,7 +73,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do
conn: conn,
site: site1
} do
site2 = insert(:site)
site2 = new_site()
params = %{
name: "pageview",
@ -302,7 +303,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do
test "blocks traffic from a domain when it's blocked", %{
conn: conn
} do
site = insert(:site, ingest_rate_limit_threshold: 0)
site = new_site(ingest_rate_limit_threshold: 0)
params = %{
domain: site.domain,
@ -1255,7 +1256,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do
describe "scroll depth tests" do
setup do
site = insert(:site)
site = new_site()
{:ok, site: site}
end
@ -1313,7 +1314,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do
describe "acquisition channel tests" do
setup do
site = insert(:site)
site = new_site()
{:ok, site: site}
end
@ -2113,7 +2114,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do
describe "custom source parsing rules" do
setup do
site = insert(:site)
site = new_site()
{:ok, site: site}
end
@ -2991,7 +2992,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do
describe "custom channel parsing rules" do
setup do
site = insert(:site)
site = new_site()
{:ok, site: site}
end
@ -3317,7 +3318,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do
describe "user_id generation" do
setup do
site = insert(:site)
site = new_site()
{:ok, site: site}
end
@ -3391,7 +3392,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do
end
test "different domain value results in different user ID", %{conn: conn, site: site1} do
site2 = insert(:site)
site2 = new_site()
params = %{
url: "https://user-id-test-domain.com/",
@ -3465,7 +3466,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do
describe "remaining" do
setup do
site = insert(:site)
site = new_site()
{:ok, site: site}
end

View File

@ -47,8 +47,7 @@ defmodule PlausibleWeb.Api.ExternalStatsController.AuthTest do
end
test "locked site - returns 402", %{conn: conn, api_key: api_key, user: user} do
site = insert(:site, members: [user])
{:ok, 1} = Plausible.Billing.SiteLocker.set_lock_status_for(user, true)
site = new_site(owner: user, locked: true)
conn
|> with_api_key(api_key)
@ -57,7 +56,7 @@ defmodule PlausibleWeb.Api.ExternalStatsController.AuthTest do
end
test "can access with correct API key and site ID", %{conn: conn, user: user, api_key: api_key} do
site = insert(:site, members: [user])
site = new_site(owner: user)
conn
|> with_api_key(api_key)
@ -74,7 +73,7 @@ defmodule PlausibleWeb.Api.ExternalStatsController.AuthTest do
end
test "can access as a super admin", %{conn: conn, api_key: api_key} do
site = insert(:site)
site = new_site()
conn
|> with_api_key(api_key)
@ -89,8 +88,7 @@ defmodule PlausibleWeb.Api.ExternalStatsController.AuthTest do
api_key: api_key,
user: user
} do
site = insert(:site, members: [user])
{:ok, 1} = Plausible.Billing.SiteLocker.set_lock_status_for(user, true)
site = new_site(owner: user, locked: true)
conn
|> with_api_key(api_key)
@ -132,7 +130,7 @@ defmodule PlausibleWeb.Api.ExternalStatsController.AuthTest do
} do
old_domain = "old.example.com"
new_domain = "new.example.com"
site = insert(:site, domain: old_domain, members: [user])
site = new_site(domain: old_domain, owner: user)
Plausible.Site.Domain.change(site, new_domain)

View File

@ -65,8 +65,7 @@ defmodule PlausibleWeb.Api.ExternalStatsController.BreakdownTest do
user: user,
site: site
} do
ep = insert(:enterprise_plan, features: [Feature.StatsAPI], user_id: user.id)
insert(:subscription, user: user, paddle_plan_id: ep.paddle_plan_id)
subscribe_to_enterprise_plan(user, features: [Feature.StatsAPI])
conn =
get(conn, "/api/v1/stats/breakdown", %{

View File

@ -1,5 +1,6 @@
defmodule PlausibleWeb.Api.ExternalStatsController.QueryTest do
use PlausibleWeb.ConnCase
use Plausible.Teams.Test
@user_id Enum.random(1000..9999)
@ -1381,7 +1382,7 @@ defmodule PlausibleWeb.Api.ExternalStatsController.QueryTest do
test "timeseries with quarter-hour timezone", %{conn: conn, user: user} do
# GMT+05:45
site = insert(:site, timezone: "Asia/Katmandu", members: [user])
site = new_site(timezone: "Asia/Katmandu", owner: user)
populate_stats(site, [
build(:pageview, timestamp: ~N[2021-01-02 05:00:00]),

View File

@ -1,4 +1,5 @@
defmodule PlausibleWeb.Api.StatsController.AuthorizationTest do
use Plausible.Teams.Test
use PlausibleWeb.ConnCase
describe "API authorization - as anonymous user" do
@ -37,21 +38,21 @@ defmodule PlausibleWeb.Api.StatsController.AuthorizationTest do
end
test "Sends 404 Not found when user does not have access to site", %{conn: conn} do
site = insert(:site)
site = new_site()
conn = get(conn, "/api/stats/#{site.domain}/main-graph")
assert conn.status == 404
end
test "returns stats for public site", %{conn: conn} do
site = insert(:site, public: true)
site = new_site(public: true)
conn = get(conn, "/api/stats/#{site.domain}/main-graph")
assert %{"plot" => _any} = json_response(conn, 200)
end
test "returns stats for a private site that the user owns", %{conn: conn, user: user} do
site = insert(:site, public: false, members: [user])
site = new_site(public: false, owner: user)
conn = get(conn, "/api/stats/#{site.domain}/main-graph")
assert %{"plot" => _any} = json_response(conn, 200)

View File

@ -99,7 +99,7 @@ defmodule PlausibleWeb.Api.StatsController.FunnelsTest do
end
test "computes all-time funnel with filters", %{conn: conn, user: user} do
site = insert(:site, stats_start_date: ~D[2020-01-01], members: [user])
site = new_site(stats_start_date: ~D[2020-01-01], owner: user)
{:ok, funnel} = setup_funnel(site, @build_funnel_with)
populate_stats(site, [

View File

@ -1,5 +1,6 @@
defmodule PlausibleWeb.Api.StatsController.MainGraphTest do
use PlausibleWeb.ConnCase
use Plausible.Teams.Test
@user_id Enum.random(1000..9999)
@ -102,9 +103,9 @@ defmodule PlausibleWeb.Api.StatsController.MainGraphTest do
test "displays hourly stats in configured timezone", %{conn: conn, user: user} do
# UTC+1
site =
insert(:site,
new_site(
domain: "tz-test.com",
members: [user],
owner: user,
timezone: "CET"
)
@ -1273,7 +1274,7 @@ defmodule PlausibleWeb.Api.StatsController.MainGraphTest do
end
test "bugfix: don't crash when timezone gap occurs", %{conn: conn, user: user} do
site = insert(:site, members: [user], timezone: "America/Santiago")
site = new_site(owner: user, timezone: "America/Santiago")
populate_stats(site, [
build(:pageview, timestamp: relative_time(minutes: -5))

View File

@ -1,4 +1,5 @@
defmodule PlausibleWeb.Api.StatsController.RegionsTest do
use Plausible.Teams.Test
use PlausibleWeb.ConnCase
describe "GET /api/stats/:domain/regions" do
@ -99,7 +100,7 @@ defmodule PlausibleWeb.Api.StatsController.RegionsTest do
# Given it's 28th Nov and there's 30 day range, the starting day falls on 29th Oct
# which coincides with daylight savings time change there:
# https://www.timeanddate.com/time/change/portugal/ponta-delgada-azores.
site = insert(:site, members: [user], timezone: "Atlantic/Azores")
site = new_site(owner: user, timezone: "Atlantic/Azores")
populate_stats(site, [
build(:pageview, timestamp: relative_time(minutes: -5))

View File

@ -1,6 +1,7 @@
defmodule PlausibleWeb.AuthControllerTest do
use PlausibleWeb.ConnCase, async: true
use Bamboo.Test
use Plausible.Teams.Test
use Plausible.Repo
import Plausible.Test.Support.HTML
@ -582,14 +583,9 @@ defmodule PlausibleWeb.AuthControllerTest do
])
insert(:google_auth, site: site, user: user)
insert(:subscription, user: user, status: Subscription.Status.deleted())
insert(:subscription, user: user, status: Subscription.Status.active())
insert(:enterprise_plan,
user: user,
paddle_plan_id: "whatever",
site_limit: 1
)
subscribe_to_growth_plan(user, status: Subscription.Status.deleted())
subscribe_to_growth_plan(user, status: Subscription.Status.active())
subscribe_to_enterprise_plan(user, site_limit: 1, subscription?: false)
{:ok, _team} = Plausible.Teams.get_or_create(user)
@ -598,6 +594,8 @@ defmodule PlausibleWeb.AuthControllerTest do
assert Repo.reload(site) == nil
assert Repo.reload(user) == nil
assert Repo.all(Plausible.Billing.Subscription) == []
assert Repo.all(Plausible.Billing.EnterprisePlan) == []
assert Repo.all(Plausible.Site.Membership) == []
assert Repo.all(Plausible.Teams.Team) == []
end

View File

@ -43,6 +43,7 @@ defmodule PlausibleWeb.BillingControllerTest do
user: user
} do
subscribe_to_plan(user, "123123")
team = team_of(user)
for _ <- 1..11, do: new_site(owner: user)
@ -50,7 +51,7 @@ defmodule PlausibleWeb.BillingControllerTest do
conn = post(conn, Routes.billing_path(conn, :change_plan, @v4_growth_plan))
subscription = Plausible.Repo.get_by(Plausible.Billing.Subscription, user_id: user.id)
subscription = Plausible.Repo.get_by(Plausible.Billing.Subscription, team_id: team.id)
assert Phoenix.Flash.get(conn.assigns.flash, :error) =~ "are exceeded: site limit"
assert subscription.paddle_plan_id == "123123"
@ -61,6 +62,7 @@ defmodule PlausibleWeb.BillingControllerTest do
user: user
} do
subscribe_to_plan(user, "123123")
team = team_of(user)
site = new_site(owner: user)
now = NaiveDateTime.utc_now()
@ -69,7 +71,7 @@ defmodule PlausibleWeb.BillingControllerTest do
conn1 = post(conn, Routes.billing_path(conn, :change_plan, @v4_growth_plan))
subscription = Plausible.Repo.get_by(Plausible.Billing.Subscription, user_id: user.id)
subscription = Plausible.Repo.get_by(Plausible.Billing.Subscription, team_id: team.id)
assert Phoenix.Flash.get(conn1.assigns.flash, :error) =~
"are exceeded: monthly pageview limit"
@ -88,10 +90,11 @@ defmodule PlausibleWeb.BillingControllerTest do
test "calls Paddle API to update subscription", %{conn: conn, user: user} do
subscribe_to_plan(user, "321321")
team = team_of(user)
post(conn, Routes.billing_path(conn, :change_plan, "123123"))
subscription = Plausible.Repo.get_by(Plausible.Billing.Subscription, user_id: user.id)
subscription = Plausible.Repo.get_by(Plausible.Billing.Subscription, team_id: team.id)
assert subscription.paddle_plan_id == "123123"
assert subscription.next_bill_date == ~D[2019-07-10]
assert subscription.next_bill_amount == "6.00"

View File

@ -8,15 +8,11 @@ defmodule PlausibleWeb.Site.InvitationControllerTest do
describe "POST /sites/invitations/:invitation_id/accept" do
test "converts the invitation into a membership", %{conn: conn, user: user} do
site = insert(:site)
owner = new_user()
site = new_site(owner: owner)
invitation =
insert(:invitation,
site_id: site.id,
inviter: build(:user),
email: user.email,
role: :admin
)
invite_guest(site, user.email, inviter: owner, role: :editor)
conn = post(conn, "/sites/invitations/#{invitation.invitation_id}/accept")
@ -31,7 +27,7 @@ defmodule PlausibleWeb.Site.InvitationControllerTest do
assert membership.role == :admin
end
@tag :team
@tag :teams
test "multiple invites per same team sync regression", %{conn: conn, user: user} do
inviter = insert(:user)
{:ok, team} = Plausible.Teams.get_or_create(inviter)
@ -82,7 +78,7 @@ defmodule PlausibleWeb.Site.InvitationControllerTest do
end
test "does not crash if clicked for the 2nd time in another tab", %{conn: conn, user: user} do
site = insert(:site)
site = new_site()
invitation =
insert(:invitation,
@ -168,15 +164,10 @@ defmodule PlausibleWeb.Site.InvitationControllerTest do
describe "POST /sites/invitations/:invitation_id/reject" do
test "rejects the invitation", %{conn: conn, user: user} do
site = insert(:site)
owner = new_user()
site = new_site(owner: owner)
invitation =
insert(:invitation,
site_id: site.id,
inviter: build(:user),
email: user.email,
role: :admin
)
invitation = invite_guest(site, user.email, inviter: owner, role: :editor)
conn = post(conn, "/sites/invitations/#{invitation.invitation_id}/reject")
@ -197,19 +188,10 @@ defmodule PlausibleWeb.Site.InvitationControllerTest do
describe "DELETE /sites/:domain/invitations/:invitation_id" do
test "removes the invitation", %{conn: conn, user: user} do
site =
insert(:site,
members: [build(:user)],
memberships: [build(:site_membership, user: user, role: :admin)]
)
invitation =
insert(:invitation,
site_id: site.id,
inviter: build(:user),
email: "jane@example.com",
role: :admin
)
owner = new_user()
site = new_site(owner: owner)
add_guest(site, user: user, role: :editor)
invitation = invite_guest(site, "jane@example.com", inviter: owner, role: :editor)
conn =
delete(
@ -223,19 +205,10 @@ defmodule PlausibleWeb.Site.InvitationControllerTest do
end
test "removes the invitation for ownership transfer", %{conn: conn, user: user} do
site =
insert(:site,
members: [build(:user)],
memberships: [build(:site_membership, user: user, role: :admin)]
)
invitation =
insert(:invitation,
site_id: site.id,
inviter: build(:user),
email: "jane@example.com",
role: :owner
)
owner = new_user()
site = new_site(owner: owner)
add_guest(site, user: user, role: :editor)
invitation = invite_transfer(site, "jane@example.com", inviter: owner)
conn =
delete(
@ -249,15 +222,11 @@ defmodule PlausibleWeb.Site.InvitationControllerTest do
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)])
owner = new_user()
site = new_site(owner: owner)
add_guest(site, user: user, role: :viewer)
invitation =
insert(:invitation,
site_id: site.id,
inviter: build(:user),
email: "jane@example.com",
role: :admin
)
invitation = invite_guest(site, "jane@example.com", inviter: owner, role: :editor)
delete(
conn,
@ -268,20 +237,14 @@ defmodule PlausibleWeb.Site.InvitationControllerTest do
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")])
new_site(owner: me)
other_user = insert(:user)
other_user = new_user()
other_site =
insert(:site, memberships: [build(:site_membership, user: other_user, role: "owner")])
other_site = new_site(owner: other_user)
invitation =
insert(:invitation,
site_id: other_site.id,
inviter: other_user,
email: "jane@example.com",
role: :admin
)
invite_guest(other_site, "jane@example.com", role: :editor, inviter: other_user)
remove_invitation_path =
Routes.invitation_path(
@ -297,7 +260,8 @@ defmodule PlausibleWeb.Site.InvitationControllerTest do
end
test "renders error for non-existent invitation", %{conn: conn, user: user} do
site = insert(:site, memberships: [build(:site_membership, user: user, role: :admin)])
site = new_site()
add_guest(site, user: user, role: :editor)
remove_invitation_path =
Routes.invitation_path(

View File

@ -48,7 +48,6 @@ defmodule PlausibleWeb.SettingsControllerTest do
@tag :ee_only
test "shows enterprise plan subscription", %{conn: conn, user: user} do
subscribe_to_plan(user, "123")
configure_enterprise_plan(user)
conn = get(conn, Routes.settings_path(conn, :subscription))
@ -117,13 +116,7 @@ defmodule PlausibleWeb.SettingsControllerTest do
conn: conn,
user: user
} do
configure_enterprise_plan(user)
insert(:subscription,
user: user,
status: Subscription.Status.past_due(),
paddle_plan_id: @configured_enterprise_plan_paddle_plan_id
)
configure_enterprise_plan(user, subscription: [status: Subscription.Status.past_due()])
doc =
conn
@ -137,13 +130,7 @@ defmodule PlausibleWeb.SettingsControllerTest do
conn: conn,
user: user
} do
configure_enterprise_plan(user)
insert(:subscription,
user: user,
status: Subscription.Status.paused(),
paddle_plan_id: @configured_enterprise_plan_paddle_plan_id
)
configure_enterprise_plan(user, subscription: [status: Subscription.Status.paused()])
doc =
conn
@ -185,8 +172,6 @@ defmodule PlausibleWeb.SettingsControllerTest do
@tag :ee_only
test "links to '/billing/choose-plan' with the text 'Change plan' for a configured enterprise plan with an existing subscription + renders cancel button",
%{conn: conn, user: user} do
insert(:subscription, paddle_plan_id: @v3_plan_id, user: user)
configure_enterprise_plan(user)
doc =
@ -207,9 +192,9 @@ defmodule PlausibleWeb.SettingsControllerTest do
@tag :ee_only
test "renders cancelled subscription notice", %{conn: conn, user: user} do
insert(:subscription,
paddle_plan_id: @v4_plan_id,
user: user,
subscribe_to_plan(
user,
@v4_plan_id,
status: :deleted,
next_bill_date: ~D[2023-01-01]
)
@ -229,11 +214,9 @@ defmodule PlausibleWeb.SettingsControllerTest do
conn: conn,
user: user
} do
insert(:subscription,
paddle_plan_id: @v4_plan_id,
user: user,
subscribe_to_plan(user, @v4_plan_id,
status: :deleted,
next_bill_date: Timex.shift(Timex.today(), days: 10)
next_bill_date: Date.shift(Date.utc_today(), day: 10)
)
notice_text =
@ -252,11 +235,11 @@ defmodule PlausibleWeb.SettingsControllerTest do
conn: conn,
user: user
} do
insert(:subscription,
paddle_plan_id: @v3_plan_id,
user: user,
subscribe_to_plan(
user,
@v3_plan_id,
status: :deleted,
next_bill_date: Timex.shift(Timex.today(), days: 10)
next_bill_date: Date.shift(Date.utc_today(), day: 10)
)
notice_text =
@ -353,11 +336,16 @@ defmodule PlausibleWeb.SettingsControllerTest do
assert element_exists?(doc, "#total_pageviews_penultimate_cycle")
end
subscribe_to_plan(user, @v4_plan_id,
status: :active,
last_bill_date: Timex.shift(Timex.now(), months: -6)
)
subscription =
subscribe_to_plan(user, @v4_plan_id,
status: :active,
last_bill_date: Timex.shift(Timex.now(), months: -6)
).subscription
user
|> team_of()
|> Plausible.Teams.with_subscription()
|> Map.fetch!(:subscription)
get(conn, Routes.settings_path(conn, :subscription))
|> html_response(200)
@ -458,12 +446,17 @@ defmodule PlausibleWeb.SettingsControllerTest do
|> html_response(200)
|> assert_usage.()
subscribe_to_plan(user, @v4_plan_id,
status: :deleted,
last_bill_date: ~D[2022-01-01],
next_bill_date: ~D[2022-02-01]
)
subscription =
subscribe_to_plan(user, @v4_plan_id,
status: :deleted,
last_bill_date: ~D[2022-01-01],
next_bill_date: ~D[2022-02-01]
).subscription
user
|> team_of()
|> Plausible.Teams.with_subscription()
|> Map.fetch!(:subscription)
conn
|> get(Routes.settings_path(conn, :subscription))
@ -1097,11 +1090,17 @@ defmodule PlausibleWeb.SettingsControllerTest do
end
end
defp configure_enterprise_plan(user) do
subscribe_to_enterprise_plan(user,
paddle_plan_id: @configured_enterprise_plan_paddle_plan_id,
monthly_pageview_limit: 20_000_000,
billing_interval: :yearly
defp configure_enterprise_plan(user, attrs \\ []) do
subscribe_to_enterprise_plan(
user,
Keyword.merge(
[
paddle_plan_id: @configured_enterprise_plan_paddle_plan_id,
monthly_pageview_limit: 20_000_000,
billing_interval: :yearly
],
attrs
)
)
end
end

View File

@ -77,7 +77,8 @@ defmodule PlausibleWeb.Site.MembershipControllerTest do
end
test "fails to create invitation with insufficient permissions", %{conn: conn, user: user} do
site = insert(:site, memberships: [build(:site_membership, user: user, role: :viewer)])
site = new_site()
add_guest(site, user: user, role: :viewer)
conn =
post(conn, "/sites/#{site.domain}/memberships/invite", %{
@ -86,17 +87,14 @@ defmodule PlausibleWeb.Site.MembershipControllerTest do
})
assert conn.status == 404
refute Repo.get_by(Plausible.Auth.Invitation, email: "john.doe@example.com")
end
test "fails to create invitation for a foreign site", %{conn: my_conn, user: me} do
_my_site = insert(:site, memberships: [build(:site_membership, user: me, role: "owner")])
_my_site = new_site(owner: me)
other_user = insert(:user)
other_user = new_user()
other_site =
insert(:site, memberships: [build(:site_membership, user: other_user, role: "owner")])
other_site = new_site(owner: other_user)
my_conn =
post(my_conn, "/sites/#{other_site.domain}/memberships/invite", %{
@ -243,8 +241,8 @@ defmodule PlausibleWeb.Site.MembershipControllerTest do
end
test "fails to transfer ownership to a foreign domain", %{conn: conn, user: user} do
insert(:site, members: [user])
foreign_site = insert(:site)
new_site(owner: user)
foreign_site = new_site()
conn =
post(conn, "/sites/#{foreign_site.domain}/transfer-ownership", %{
@ -252,8 +250,6 @@ defmodule PlausibleWeb.Site.MembershipControllerTest do
})
assert conn.status == 404
refute Repo.get_by(Plausible.Auth.Invitation, email: "john.doe@example.com")
end
test "fails to transfer ownership to invited user with proper error message", ctx do
@ -295,35 +291,12 @@ defmodule PlausibleWeb.Site.MembershipControllerTest do
assert_team_membership(collaborator, site.team, :viewer)
end
@tag :teams
test "syncs role update to team", %{conn: conn, user: user} do
admin = insert(:user)
site =
insert(:site,
memberships: [
build(:site_membership, user: user, role: :owner),
build(:site_membership, user: admin, role: :admin)
]
)
|> Plausible.Teams.load_for_site()
team_membership =
insert(:team_membership, user: admin, team: site.team, role: :guest)
guest_membership =
insert(:guest_membership, team_membership: team_membership, site: site, role: :editor)
put(conn, "/sites/#{site.domain}/memberships/u/#{admin.id}/role/viewer")
assert Repo.reload!(guest_membership).role == :viewer
end
test "can downgrade yourself from admin to viewer, redirects to stats instead", %{
test "can downgrade yourself from admin to viewer, redirects to stats", %{
conn: conn,
user: user
} do
site = insert(:site, memberships: [build(:site_membership, user: user, role: :admin)])
site = new_site()
add_guest(site, user: user, role: :editor)
conn = put(conn, "/sites/#{site.domain}/memberships/u/#{user.id}/role/viewer")
@ -352,7 +325,7 @@ defmodule PlausibleWeb.Site.MembershipControllerTest do
conn: conn,
user: user
} do
site = insert(:site, memberships: [build(:site_membership, user: user, role: :owner)])
site = new_site(owner: user)
conn = put(conn, "/sites/#{site.domain}/memberships/u/#{user.id}/role/admin")
@ -375,7 +348,7 @@ defmodule PlausibleWeb.Site.MembershipControllerTest do
conn = put(conn, "/sites/#{site.domain}/memberships/u/#{viewer.id}/role/admin")
assert_team_membership(viewer, site.team, :editor)
assert_team_membership(viewer, site.team, :viewer)
assert redirected_to(conn) == "/#{URI.encode_www_form(site.domain)}/settings/people"
end
@ -403,105 +376,17 @@ defmodule PlausibleWeb.Site.MembershipControllerTest do
refute Repo.exists?(from sm in Plausible.Site.Membership, where: sm.user_id == ^admin.id)
end
@tag :teams
test "syncs member removal to team", %{conn: conn, user: user} do
admin = insert(:user)
site =
insert(:site,
memberships: [
build(:site_membership, user: user, role: :owner),
build(:site_membership, user: admin, role: :admin)
]
)
|> Plausible.Teams.load_for_site()
team_membership =
insert(:team_membership, user: admin, team: site.team, role: :guest)
guest_membership =
insert(:guest_membership, team_membership: team_membership, site: site, role: :editor)
conn = delete(conn, "/sites/#{site.domain}/memberships/u/#{admin.id}")
assert Phoenix.Flash.get(conn.assigns.flash, :success) =~ "has been removed"
refute Repo.reload(guest_membership)
refute Repo.reload(team_membership)
end
@tag :teams
test "sync retains team guest membership when there's another guest membership on it", %{
conn: conn,
user: user
} do
admin = insert(:user)
site =
insert(:site,
memberships: [
build(:site_membership, user: user, role: :owner),
build(:site_membership, user: admin, role: :admin)
]
)
|> Plausible.Teams.load_for_site()
another_site =
insert(:site,
team: site.team,
memberships: [
build(:site_membership, user: user, role: :owner),
build(:site_membership, user: admin, role: :admin)
]
)
|> Plausible.Teams.load_for_site()
team_membership =
insert(:team_membership, user: admin, team: site.team, role: :guest)
guest_membership =
insert(:guest_membership, team_membership: team_membership, site: site, role: :editor)
another_guest_membership =
insert(:guest_membership,
team_membership: team_membership,
site: another_site,
role: :editor
)
conn = delete(conn, "/sites/#{site.domain}/memberships/u/#{admin.id}")
assert Phoenix.Flash.get(conn.assigns.flash, :success) =~ "has been removed"
refute Repo.reload(guest_membership)
assert Repo.reload(another_guest_membership)
assert Repo.reload(team_membership)
end
test "fails to remove a member from a foreign site", %{conn: conn, user: user} do
foreign_site =
insert(:site,
memberships: [
build(:site_membership, user: build(:user), role: :admin)
]
)
foreign_member = new_user()
foreign_site = new_site()
add_guest(foreign_site, user: foreign_member, role: :editor)
[foreign_membership] = foreign_site.memberships
site = new_site(owner: user)
site =
insert(:site,
memberships: [
build(:site_membership, user: user, role: :owner)
]
)
conn = delete(conn, "/sites/#{site.domain}/memberships/u/#{foreign_membership.user_id}")
conn = delete(conn, "/sites/#{site.domain}/memberships/u/#{foreign_member.id}")
assert Phoenix.Flash.get(conn.assigns.flash, :error) ==
"Failed to find membership to remove"
assert Repo.exists?(
from sm in Plausible.Site.Membership,
where: sm.user_id == ^foreign_membership.user.id
)
end
test "notifies the user who has been removed via email", %{conn: conn, user: user} do

View File

@ -42,12 +42,11 @@ defmodule PlausibleWeb.SiteControllerTest do
conn: conn,
user: user
} do
ep = insert(:enterprise_plan, user: user)
insert(:subscription, user: user, paddle_plan_id: ep.paddle_plan_id)
subscribe_to_enterprise_plan(user)
insert(:site, members: [user])
insert(:site, members: [user])
insert(:site, members: [user])
new_site(owner: user)
new_site(owner: user)
new_site(owner: user)
conn = get(conn, "/sites/new")
refute html_response(conn, 200) =~ "is limited to"
@ -342,9 +341,9 @@ defmodule PlausibleWeb.SiteControllerTest do
conn: conn,
user: user
} do
ep = insert(:enterprise_plan, user: user, site_limit: 1)
insert(:subscription, user: user, paddle_plan_id: ep.paddle_plan_id)
insert_list(2, :site, members: [user])
subscribe_to_enterprise_plan(user, site_limit: 1)
new_site(owner: user)
new_site(owner: user)
conn =
post(conn, "/sites", %{
@ -622,19 +621,17 @@ defmodule PlausibleWeb.SiteControllerTest do
end
test "fails to make site public with insufficient permissions", %{conn: conn, user: user} do
site = insert(:site, memberships: [build(:site_membership, user: user, role: :viewer)])
site = new_site()
add_guest(site, user: user, role: :viewer)
conn = post(conn, "/sites/#{site.domain}/make-public")
assert conn.status == 404
refute Repo.get(Plausible.Site, site.id).public
end
test "fails to make foreign site public", %{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")])
_my_site = new_site(owner: me)
other_user = new_user()
other_site = new_site(owner: other_user)
my_conn = post(my_conn, "/sites/#{other_site.domain}/make-public")
assert my_conn.status == 404
@ -660,7 +657,7 @@ defmodule PlausibleWeb.SiteControllerTest do
setup [:create_user, :log_in, :create_site]
test "deletes the site", %{conn: conn, user: user} do
site = insert(:site, members: [user])
site = new_site(owner: user)
insert(:google_auth, user: user, site: site)
insert(:spike_notification, site: site)
insert(:drop_notification, site: site)
@ -671,7 +668,8 @@ defmodule PlausibleWeb.SiteControllerTest do
end
test "fails to delete a site with insufficient permissions", %{conn: conn, user: user} do
site = insert(:site, memberships: [build(:site_membership, user: user, role: :viewer)])
site = new_site()
add_guest(site, user: user, role: :viewer)
insert(:google_auth, user: user, site: site)
insert(:spike_notification, site: site)
@ -682,12 +680,9 @@ defmodule PlausibleWeb.SiteControllerTest do
end
test "fails to delete a foreign site", %{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")])
_my_site = new_site(owner: me)
other_user = new_user()
other_site = new_site(owner: other_user)
insert(:google_auth, user: other_user, site: other_site)
insert(:spike_notification, site: other_site)
@ -734,7 +729,7 @@ defmodule PlausibleWeb.SiteControllerTest do
conn: conn,
user: user
} do
other_site = insert(:site)
other_site = new_site()
insert(:google_auth, user: user, site: other_site)
conn = delete(conn, "/#{URI.encode_www_form(other_site.domain)}/settings/google-search")
@ -907,7 +902,7 @@ defmodule PlausibleWeb.SiteControllerTest do
end
test "displays Continue with Google link", %{conn: conn, user: user} do
site = insert(:site, domain: "notconnectedyet.example.com", members: [user])
site = new_site(domain: "notconnectedyet.example.com", owner: user)
conn = get(conn, "/#{site.domain}/settings/integrations")
resp = html_response(conn, 200)
@ -1026,13 +1021,8 @@ defmodule PlausibleWeb.SiteControllerTest do
conn: conn0,
conn_with_url: conn_with_url
} do
site =
insert(:site,
memberships: [
build(:site_membership, user: build(:user), role: :owner),
build(:site_membership, user: user, role: :admin)
]
)
site = new_site()
add_guest(site, user: user, role: :editor)
conn =
put(
@ -1082,8 +1072,8 @@ defmodule PlausibleWeb.SiteControllerTest do
conn: conn0,
conn_with_url: conn_with_url
} do
site = insert(:site)
insert(:site_membership, user: user, site: site, role: :viewer)
site = new_site()
add_guest(site, user: user, role: :viewer)
conn =
put(
@ -1106,8 +1096,8 @@ defmodule PlausibleWeb.SiteControllerTest do
conn: conn0,
conn_with_url: conn_with_url
} do
site = insert(:site)
insert(:site_membership, user: user, site: site, role: :admin)
site = new_site()
add_guest(site, user: user, role: :editor)
setting = :funnels_enabled
@ -1172,7 +1162,7 @@ defmodule PlausibleWeb.SiteControllerTest do
end
test "fails to delete the weekly report record for a foreign site", %{conn: conn} do
site = insert(:site)
site = new_site()
insert(:weekly_report, site: site)
post(conn, "/sites/#{site.domain}/weekly-report/disable")
@ -1207,7 +1197,7 @@ defmodule PlausibleWeb.SiteControllerTest do
end
test "fails to remove a recipient from the weekly report in a foreign website", %{conn: conn} do
site = insert(:site)
site = new_site()
insert(:weekly_report, site: site, recipients: ["recipient@email.com"])
conn1 = delete(conn, "/sites/#{site.domain}/weekly-report/recipients/recipient@email.com")
@ -1290,7 +1280,7 @@ defmodule PlausibleWeb.SiteControllerTest do
test "fails to remove a recipient from the monthly report in a foreign website", %{
conn: conn
} do
site = insert(:site)
site = new_site()
insert(:monthly_report, site: site, recipients: ["recipient@email.com"])
conn1 = delete(conn, "/sites/#{site.domain}/monthly-report/recipients/recipient@email.com")
@ -1419,7 +1409,7 @@ defmodule PlausibleWeb.SiteControllerTest do
test "fails to remove a recipient from the #{type} notification in a foreign website", %{
conn: conn
} do
site = insert(:site)
site = new_site()
insert(:"#{unquote(type)}_notification", site: site, recipients: ["recipient@email.com"])
conn =

View File

@ -39,7 +39,7 @@ defmodule PlausibleWeb.StatsControllerTest do
end
test "plausible.io live demo - shows site stats", %{conn: conn} do
site = insert(:site, domain: "plausible.io", public: true)
site = new_site(domain: "plausible.io", public: true)
populate_stats(site, [build(:pageview)])
conn = get(conn, "/#{site.domain}")
@ -66,7 +66,7 @@ defmodule PlausibleWeb.StatsControllerTest do
test "public site - no stats with skip_to_dashboard", %{
conn: conn
} do
insert(:site, domain: "some-other-public-site.io", public: true)
new_site(domain: "some-other-public-site.io", public: true)
conn = get(conn, "/some-other-public-site.io?skip_to_dashboard=true")
resp = html_response(conn, 200)
@ -103,13 +103,13 @@ defmodule PlausibleWeb.StatsControllerTest do
end
test "shows locked page if page is locked", %{conn: conn, user: user} do
locked_site = insert(:site, locked: true, members: [user])
locked_site = new_site(locked: true, owner: user)
conn = get(conn, "/" <> locked_site.domain)
assert html_response(conn, 200) =~ "Dashboard locked"
end
test "can not view stats of someone else's website", %{conn: conn} do
site = insert(:site)
site = new_site()
conn = get(conn, "/" <> site.domain)
assert html_response(conn, 404) =~ "There's nothing here"
end
@ -125,7 +125,7 @@ defmodule PlausibleWeb.StatsControllerTest do
setup [:create_user, :make_user_super_admin, :log_in]
test "can view a private dashboard with stats", %{conn: conn} do
site = insert(:site)
site = new_site()
populate_stats(site, [build(:pageview)])
conn = get(conn, "/" <> site.domain)
@ -133,15 +133,15 @@ defmodule PlausibleWeb.StatsControllerTest do
end
test "can enter verification when site is without stats", %{conn: conn} do
site = insert(:site)
site = new_site()
conn = get(conn, conn |> get("/" <> site.domain) |> redirected_to())
assert html_response(conn, 200) =~ "Verifying your installation"
end
test "can view a private locked dashboard with stats", %{conn: conn} do
user = insert(:user)
site = insert(:site, locked: true, members: [user])
user = new_user()
site = new_site(locked: true, owner: user)
populate_stats(site, [build(:pageview)])
conn = get(conn, "/" <> site.domain)
@ -153,15 +153,15 @@ defmodule PlausibleWeb.StatsControllerTest do
end
test "can view private locked verification without stats", %{conn: conn} do
user = insert(:user)
site = insert(:site, locked: true, members: [user])
user = new_user()
site = new_site(locked: true, owner: user)
conn = get(conn, conn |> get("/#{site.domain}") |> redirected_to())
assert html_response(conn, 200) =~ "Verifying your installation"
end
test "can view a locked public dashboard", %{conn: conn} do
site = insert(:site, locked: true, public: true)
site = new_site(locked: true, public: true)
populate_stats(site, [build(:pageview)])
conn = get(conn, "/" <> site.domain)
@ -172,7 +172,7 @@ defmodule PlausibleWeb.StatsControllerTest do
end
test "shows CRM link to the site", %{conn: conn} do
site = insert(:site)
site = new_site()
conn = get(conn, conn |> get("/" <> site.domain) |> redirected_to())
assert html_response(conn, 200) =~ "/crm/sites/site/#{site.id}"
end
@ -603,7 +603,7 @@ defmodule PlausibleWeb.StatsControllerTest do
describe "GET /:domain/export - via shared link" do
test "exports data in zipped csvs", %{conn: conn} do
site = insert(:site, domain: "new-site.com")
site = new_site(domain: "new-site.com")
link = insert(:shared_link, site: site)
populate_exported_stats(site)
@ -875,7 +875,7 @@ defmodule PlausibleWeb.StatsControllerTest do
test "logs anonymous user in straight away if the link is not password-protected", %{
conn: conn
} do
site = insert(:site, domain: "test-site.com")
site = new_site(domain: "test-site.com")
link = insert(:shared_link, site: site)
conn = get(conn, "/share/test-site.com/?auth=#{link.slug}")
@ -885,7 +885,7 @@ defmodule PlausibleWeb.StatsControllerTest do
test "returns page with X-Frame-Options disabled so it can be embedded in an iframe", %{
conn: conn
} do
site = insert(:site, domain: "test-site.com")
site = new_site(domain: "test-site.com")
link = insert(:shared_link, site: site)
conn = get(conn, "/share/test-site.com/?auth=#{link.slug}")
@ -897,7 +897,7 @@ defmodule PlausibleWeb.StatsControllerTest do
test "returns page embedded page", %{
conn: conn
} do
site = insert(:site, domain: "test-site.com")
site = new_site(domain: "test-site.com")
link = insert(:shared_link, site: site)
conn = get(conn, "/share/test-site.com/?auth=#{link.slug}&embed=true")
@ -959,7 +959,7 @@ defmodule PlausibleWeb.StatsControllerTest do
describe "POST /share/:slug/authenticate" do
test "logs anonymous user in with correct password", %{conn: conn} do
site = insert(:site, domain: "test-site.com")
site = new_site(domain: "test-site.com")
link =
insert(:shared_link, site: site, password_hash: Plausible.Auth.Password.hash("password"))

View File

@ -867,9 +867,12 @@ defmodule PlausibleWeb.Live.ChoosePlanTest do
test "highlights recommended tier if subscription expired and no days are paid for anymore",
%{conn: conn, user: user} do
user.subscription
user
|> team_of()
|> Repo.preload(:subscription)
|> Map.fetch!(:subscription)
|> Subscription.changeset(%{next_bill_date: Timex.shift(Timex.now(), months: -2)})
|> Repo.update()
|> Repo.update!()
{:ok, _lv, doc} = get_liveview(conn)
assert text_of_element(doc, @growth_highlight_pill) == "Recommended"

View File

@ -64,8 +64,8 @@ defmodule PlausibleWeb.Plugs.AuthorizePublicAPITest do
@tag :ee_only
test "halts with error when upgrade is required", %{conn: conn} do
user = insert(:user, trial_expiry_date: nil)
site = insert(:site, members: [user])
user = new_user() |> subscribe_to_enterprise_plan(paddle_plan_id: "123321", features: [])
site = new_site(owner: user)
api_key = insert(:api_key, user: user)
conn =
@ -82,8 +82,8 @@ defmodule PlausibleWeb.Plugs.AuthorizePublicAPITest do
end
test "halts with error when site is locked", %{conn: conn} do
user = insert(:user)
site = insert(:site, members: [user], locked: true)
user = new_user()
site = new_site(owner: user, locked: true)
api_key = insert(:api_key, user: user)
conn =
@ -202,9 +202,9 @@ defmodule PlausibleWeb.Plugs.AuthorizePublicAPITest do
@tag :ee_only
test "passes for super admin user even if not a member of the requested site", %{conn: conn} do
user = insert(:user)
user = new_user()
patch_env(:super_admin_user_ids, [user.id])
site = insert(:site, locked: true)
site = new_site(locked: true)
api_key = insert(:api_key, user: user)
conn =

View File

@ -1,5 +1,6 @@
defmodule PlausibleWeb.Plugs.AuthorizeSiteAccessTest do
use PlausibleWeb.ConnCase, async: false
use Plausible.Teams.Test
alias PlausibleWeb.Plugs.AuthorizeSiteAccess
setup [:create_user, :log_in, :create_site]
@ -14,10 +15,10 @@ defmodule PlausibleWeb.Plugs.AuthorizeSiteAccessTest do
[],
:all_roles,
{:all_roles, nil},
{[:public, :viewer, :admin, :super_admin, :owner], nil}
{[:public, :viewer, :admin, :editor, :super_admin, :owner], nil}
] do
test "init resolves to expected options with argument #{inspect(init_argument)}" do
assert {[:public, :viewer, :admin, :super_admin, :owner], nil} ==
assert {[:public, :viewer, :admin, :editor, :super_admin, :owner], nil} ==
AuthorizeSiteAccess.init(unquote(init_argument))
end
end
@ -46,7 +47,7 @@ defmodule PlausibleWeb.Plugs.AuthorizeSiteAccessTest do
test "rejects user completely unrelated to the site", %{conn: conn} do
opts = AuthorizeSiteAccess.init(:all_roles)
site = insert(:site, members: [build(:user)])
site = new_site()
conn =
conn
@ -98,7 +99,7 @@ defmodule PlausibleWeb.Plugs.AuthorizeSiteAccessTest do
conn: conn,
site: site
} do
other_site = insert(:site, members: [build(:user)])
other_site = new_site()
opts = AuthorizeSiteAccess.init([:admin, :owner])
@ -116,7 +117,8 @@ defmodule PlausibleWeb.Plugs.AuthorizeSiteAccessTest do
end
test "returns 404 with custom error message for failed API routes", %{conn: conn, user: user} do
site = insert(:site, memberships: [build(:site_membership, user: user, role: :viewer)])
site = new_site()
add_guest(site, user: user, role: :viewer)
opts = AuthorizeSiteAccess.init([:admin, :owner, :super_admin])
@ -137,7 +139,7 @@ defmodule PlausibleWeb.Plugs.AuthorizeSiteAccessTest do
conn: conn,
site: site
} do
shared_link_other_site = insert(:shared_link, site: build(:site))
shared_link_other_site = insert(:shared_link, site: new_site())
params = %{"shared_link" => %{"name" => "some name"}}
@ -160,7 +162,7 @@ defmodule PlausibleWeb.Plugs.AuthorizeSiteAccessTest do
site: site
} do
shared_link = insert(:shared_link, site: site)
other_site = insert(:site, members: [user])
other_site = new_site(owner: user)
opts = AuthorizeSiteAccess.init([:super_admin, :admin, :owner])
@ -200,8 +202,8 @@ defmodule PlausibleWeb.Plugs.AuthorizeSiteAccessTest do
end
test "rejects user on mismatched membership role", %{conn: conn, user: user} do
site =
insert(:site, memberships: [build(:site_membership, user: user, role: :admin)])
site = new_site()
add_guest(site, user: user, role: :editor)
opts = AuthorizeSiteAccess.init([:owner])
@ -215,10 +217,26 @@ defmodule PlausibleWeb.Plugs.AuthorizeSiteAccessTest do
assert html_response(conn, 404)
end
for role <- [:viewer, :admin, :owner] do
test "allows user based on ownership", %{conn: conn, user: user} do
site = new_site(owner: user)
opts = AuthorizeSiteAccess.init([:owner])
conn =
conn
|> bypass_through(PlausibleWeb.Router)
|> get("/plug-tests/#{site.domain}/with-domain")
|> AuthorizeSiteAccess.call(opts)
refute conn.halted
assert conn.assigns.site.id == site.id
assert conn.assigns.current_user_role == :owner
end
for role <- [:viewer, :editor] do
test "allows user based on their #{role} membership", %{conn: conn, user: user} do
site =
insert(:site, memberships: [build(:site_membership, user: user, role: unquote(role))])
site = new_site()
add_guest(site, user: user, role: unquote(role))
opts = AuthorizeSiteAccess.init([unquote(role)])
@ -236,7 +254,7 @@ defmodule PlausibleWeb.Plugs.AuthorizeSiteAccessTest do
@tag :ee_only
test "allows user based on their superadmin status", %{conn: conn, user: user} do
site = insert(:site, members: [build(:user)])
site = new_site()
patch_env(:super_admin_user_ids, [user.id])
@ -254,7 +272,7 @@ defmodule PlausibleWeb.Plugs.AuthorizeSiteAccessTest do
end
test "allows user based on website visibility (authenticated user)", %{conn: conn} do
site = insert(:site, members: [build(:user)], public: true)
site = new_site(public: true)
opts = AuthorizeSiteAccess.init([:public])
@ -284,7 +302,7 @@ defmodule PlausibleWeb.Plugs.AuthorizeSiteAccessTest do
end
test "allows user based on shared link auth (authenticated user)", %{conn: conn} do
site = insert(:site, members: [build(:user)])
site = new_site()
shared_link = insert(:shared_link, site: site)
opts = AuthorizeSiteAccess.init([:public])

View File

@ -46,22 +46,40 @@ defmodule Plausible.Teams.Test do
end
def new_user(args \\ []) do
{team_args, args} = Keyword.pop(args, :team, [])
user = insert(:user, args)
if user.trial_expiry_date do
{:ok, _team} = Teams.get_or_create(user)
{:ok, team} = Teams.get_or_create(user)
team_args = Keyword.merge(team_args, trial_expiry_date: user.trial_expiry_date)
team
|> Ecto.Changeset.change(team_args)
|> Repo.update!()
end
Repo.preload(user, team_memberships: :team)
end
def team_of(%{team_memberships: [%{role: :owner, team: %Teams.Team{} = team}]}) do
team
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) do
def team_of(user, opts) do
{:ok, team} = Plausible.Teams.get_by_owner(user)
team
if opts[:with_subscription?] do
Plausible.Teams.with_subscription(team)
else
team
end
end
def add_guest(site, args \\ []) do
@ -121,15 +139,21 @@ defmodule Plausible.Teams.Test do
old_model_invitation
end
def invite_transfer(site, invitee, args \\ []) do
def invite_transfer(site, invitee_or_email, args \\ []) do
inviter = Keyword.fetch!(args, :inviter)
email =
case invitee_or_email do
%{email: email} -> email
email when is_binary(email) -> email
end
old_model_invitation =
insert(:invitation, email: invitee.email, inviter: inviter, role: :owner, site: site)
insert(:invitation, email: email, inviter: inviter, role: :owner, site: site)
insert(:site_transfer,
transfer_id: old_model_invitation.invitation_id,
email: invitee.email,
email: email,
site: site,
initiator: inviter
)
@ -170,8 +194,7 @@ defmodule Plausible.Teams.Test do
{:ok, team} = Teams.get_or_create(user)
attrs = Keyword.merge([user: user, team: team, paddle_plan_id: paddle_plan_id], attrs)
subscription =
insert(:subscription, attrs)
subscription = insert(:subscription, attrs)
%{user | subscription: subscription}
end
@ -180,14 +203,21 @@ defmodule Plausible.Teams.Test 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([user: user, team: team], attrs))
if subscription? do
insert(:subscription,
team: team,
user: user,
paddle_plan_id: enterprise_plan.paddle_plan_id
insert(
:subscription,
Keyword.merge(
[
team: team,
user: user,
paddle_plan_id: enterprise_plan.paddle_plan_id
],
subscription_attrs
)
)
end

View File

@ -431,13 +431,13 @@ defmodule Plausible.Workers.CheckUsageTest do
}
end)
enterprise_plan = insert(:enterprise_plan, user: user, monthly_pageview_limit: 1_000_000)
insert(:subscription,
user: user,
paddle_plan_id: enterprise_plan.paddle_plan_id,
last_bill_date: Timex.shift(Timex.today(), days: -1),
status: unquote(status)
subscribe_to_enterprise_plan(
user,
monthly_pageview_limit: 1_000_000,
subscription: [
last_bill_date: Timex.shift(Timex.today(), days: -1),
status: unquote(status)
]
)
CheckUsage.perform(nil, usage_stub)
@ -461,19 +461,18 @@ defmodule Plausible.Workers.CheckUsageTest do
}
end)
enterprise_plan = insert(:enterprise_plan, user: user, site_limit: 2)
insert(:site, members: [user])
insert(:site, members: [user])
insert(:site, members: [user])
insert(:subscription,
user: user,
paddle_plan_id: enterprise_plan.paddle_plan_id,
last_bill_date: Timex.shift(Timex.today(), days: -1),
status: unquote(status)
subscribe_to_enterprise_plan(user,
site_limit: 2,
subscription: [
last_bill_date: Timex.shift(Timex.today(), days: -1),
status: unquote(status)
]
)
new_site(owner: user)
new_site(owner: user)
new_site(owner: user)
CheckUsage.perform(nil, usage_stub)
assert_email_delivered_with(
@ -482,6 +481,33 @@ defmodule Plausible.Workers.CheckUsageTest do
)
end
test "manual lock grace period is synced with teams", %{user: user} do
usage_stub =
Plausible.Billing.Quota.Usage
|> stub(:monthly_pageview_usage, fn _user ->
%{
penultimate_cycle: %{date_range: @date_range, total: 1_100_000},
last_cycle: %{date_range: @date_range, total: 1_100_000}
}
end)
subscribe_to_enterprise_plan(
user,
monthly_pageview_limit: 1_000_000,
subscription: [
last_bill_date: Timex.shift(Timex.today(), days: -1),
status: unquote(status)
]
)
CheckUsage.perform(nil, usage_stub)
assert user = Repo.reload!(user)
team = assert_team_exists(user)
assert team.grace_period.manual_lock == user.grace_period.manual_lock
assert team.grace_period.manual_lock == true
end
test "starts grace period when plan is outgrown", %{user: user} do
usage_stub =
Plausible.Billing.Quota.Usage
@ -492,46 +518,18 @@ defmodule Plausible.Workers.CheckUsageTest do
}
end)
enterprise_plan = insert(:enterprise_plan, user: user, monthly_pageview_limit: 1_000_000)
insert(:subscription,
user: user,
paddle_plan_id: enterprise_plan.paddle_plan_id,
last_bill_date: Timex.shift(Timex.today(), days: -1),
status: unquote(status)
subscribe_to_enterprise_plan(
user,
monthly_pageview_limit: 1_000_000,
subscription: [
last_bill_date: Timex.shift(Timex.today(), days: -1),
status: unquote(status)
]
)
CheckUsage.perform(nil, usage_stub)
assert user |> Repo.reload() |> Plausible.Auth.GracePeriod.active?()
end
@tag :teams
test "manual lock grace period is synced with teams", %{user: user} do
usage_stub =
Plausible.Billing.Quota.Usage
|> stub(:monthly_pageview_usage, fn _user ->
%{
penultimate_cycle: %{date_range: @date_range, total: 1_100_000},
last_cycle: %{date_range: @date_range, total: 1_100_000}
}
end)
enterprise_plan = insert(:enterprise_plan, user: user, monthly_pageview_limit: 1_000_000)
insert(:subscription,
user: user,
paddle_plan_id: enterprise_plan.paddle_plan_id,
last_bill_date: Timex.shift(Timex.today(), days: -1),
status: unquote(status)
)
CheckUsage.perform(nil, usage_stub)
assert user = Repo.reload!(user)
team = assert_team_exists(user)
assert team.grace_period.manual_lock == user.grace_period.manual_lock
assert team.grace_period.manual_lock == true
end
end
end

View File

@ -1,6 +1,7 @@
defmodule Plausible.Workers.SendEmailReportTest do
use Plausible.DataCase
use Bamboo.Test
use Plausible.Teams.Test
use Oban.Testing, repo: Plausible.Repo
import Plausible.Test.Support.HTML
alias Plausible.Workers.SendEmailReport
@ -11,7 +12,7 @@ defmodule Plausible.Workers.SendEmailReportTest do
describe "weekly reports" do
test "sends weekly report to all recipients" do
site = insert(:site, domain: "test-site.com", timezone: "US/Eastern")
site = new_site(domain: "test-site.com", timezone: "US/Eastern")
insert(:weekly_report, site: site, recipients: ["user@email.com", "user2@email.com"])
perform_job(SendEmailReport, %{"site_id" => site.id, "interval" => "weekly"})
@ -33,7 +34,7 @@ defmodule Plausible.Workers.SendEmailReportTest do
end
test "does not crash when weekly report has been deleted since scheduling job" do
site = insert(:site, domain: "test-site.com", timezone: "US/Eastern")
site = new_site(domain: "test-site.com", timezone: "US/Eastern")
assert :discard =
perform_job(SendEmailReport, %{"site_id" => site.id, "interval" => "weekly"})
@ -41,9 +42,7 @@ defmodule Plausible.Workers.SendEmailReportTest do
test "calculates timezone correctly" do
site =
insert(:site,
timezone: "US/Eastern"
)
new_site(timezone: "US/Eastern")
insert(:weekly_report, site: site, recipients: ["user@email.com"])
@ -81,7 +80,7 @@ defmodule Plausible.Workers.SendEmailReportTest do
test "includes the correct stats" do
now = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second)
site = insert(:site, domain: "test-site.com", inserted_at: Timex.shift(now, days: -8))
site = new_site(domain: "test-site.com", inserted_at: Timex.shift(now, days: -8))
insert(:weekly_report, site: site, recipients: ["user@email.com"])
populate_stats(site, [
@ -114,7 +113,7 @@ defmodule Plausible.Workers.SendEmailReportTest do
week_ago = now |> Timex.shift(days: -7)
two_weeks_ago = now |> Timex.shift(days: -14)
site = insert(:site, inserted_at: Timex.shift(now, days: -15))
site = new_site(inserted_at: Timex.shift(now, days: -15))
insert(:weekly_report, site: site, recipients: ["user@email.com"])
populate_stats(site, [
@ -149,7 +148,7 @@ defmodule Plausible.Workers.SendEmailReportTest do
week_ago = now |> Timex.shift(days: -7)
two_weeks_ago = now |> Timex.shift(days: -14)
site = insert(:site, inserted_at: Timex.shift(now, days: -15))
site = new_site(inserted_at: Timex.shift(now, days: -15))
insert(:weekly_report, site: site, recipients: ["user@email.com"])
populate_stats(site, [
@ -184,7 +183,7 @@ defmodule Plausible.Workers.SendEmailReportTest do
week_ago = now |> Timex.shift(days: -7)
two_weeks_ago = now |> Timex.shift(days: -14)
site = insert(:site, inserted_at: Timex.shift(now, days: -15))
site = new_site(inserted_at: Timex.shift(now, days: -15))
insert(:weekly_report, site: site, recipients: ["user@email.com"])
populate_stats(site, [
@ -215,7 +214,7 @@ defmodule Plausible.Workers.SendEmailReportTest do
describe "monthly_reports" do
test "sends monthly report to all recipients" do
site = insert(:site, domain: "test-site.com", timezone: "US/Eastern")
site = new_site(domain: "test-site.com", timezone: "US/Eastern")
insert(:monthly_report, site: site, recipients: ["user@email.com", "user2@email.com"])
last_month =
@ -238,7 +237,7 @@ defmodule Plausible.Workers.SendEmailReportTest do
end
test "does not crash when monthly report has been deleted since scheduling job" do
site = insert(:site, domain: "test-site.com", timezone: "US/Eastern")
site = new_site(domain: "test-site.com", timezone: "US/Eastern")
assert :discard =
perform_job(SendEmailReport, %{"site_id" => site.id, "interval" => "monthly"})

View File

@ -2,13 +2,14 @@ defmodule Plausible.Workers.SendSiteSetupEmailsTest do
use Plausible.DataCase, async: true
use Bamboo.Test
use Oban.Testing, repo: Plausible.Repo
use Plausible.Teams.Test
alias Plausible.Workers.SendSiteSetupEmails
describe "when user has not managed to set up the site" do
test "does not send an email 47 hours after site creation" do
user = insert(:user)
insert(:site, members: [user], inserted_at: hours_ago(47))
user = new_user()
new_site(owner: user, inserted_at: hours_ago(47))
perform_job(SendSiteSetupEmails, %{})
@ -16,8 +17,8 @@ defmodule Plausible.Workers.SendSiteSetupEmailsTest do
end
test "sends a setup help email 48 hours after site has been created" do
user = insert(:user)
insert(:site, members: [user], inserted_at: hours_ago(49))
user = new_user()
new_site(owner: user, inserted_at: hours_ago(49))
perform_job(SendSiteSetupEmails, %{})
@ -39,9 +40,8 @@ defmodule Plausible.Workers.SendSiteSetupEmailsTest do
describe "when user has managed to set up their site" do
test "sends the setup completed email as soon as possible" do
user = insert(:user)
site = insert(:site, members: [user])
user = new_user()
site = new_site(owner: user)
populate_stats(site, [build(:pageview)])
@ -54,8 +54,8 @@ defmodule Plausible.Workers.SendSiteSetupEmailsTest do
end
test "sends the setup completed email after the help email has been sent" do
user = insert(:user)
site = insert(:site, members: [user], inserted_at: hours_ago(49))
user = new_user()
site = new_site(owner: user, inserted_at: hours_ago(49))
perform_job(SendSiteSetupEmails, %{})
@ -76,7 +76,7 @@ defmodule Plausible.Workers.SendSiteSetupEmailsTest do
describe "trial user who has not set up a website" do
test "does not send an email before 48h have passed" do
insert(:user, inserted_at: hours_ago(47))
new_user(inserted_at: hours_ago(47))
perform_job(SendSiteSetupEmails, %{})
@ -84,7 +84,7 @@ defmodule Plausible.Workers.SendSiteSetupEmailsTest do
end
test "sends the create site email after 48h" do
user = insert(:user, inserted_at: hours_ago(49))
user = new_user(inserted_at: hours_ago(49))
perform_job(SendSiteSetupEmails, %{})