From dd1d74ccb7a3ecb60be2941f9cce7599eed056e0 Mon Sep 17 00:00:00 2001
From: RobertJoonas <56999674+RobertJoonas@users.noreply.github.com>
Date: Mon, 17 Jun 2024 09:25:46 +0300
Subject: [PATCH] Always recommend a suitable plan on the choose-plan page
(#4222)
* pull last_bill_date from paddle sandbox in mix task
* move cycle usage checks to Quota module
* move quota.ex to a subfolder
* split up Quota module
* set choose-plan pageview slider according to usage
* silence credo
---
lib/mix/tasks/pull_sandbox_subscription.ex | 1 +
lib/plausible/billing/feature.ex | 2 +-
lib/plausible/billing/plans.ex | 2 +-
lib/plausible/billing/qouta/limits.ex | 120 +++++++++
lib/plausible/billing/qouta/quota.ex | 123 +++++++++
.../billing/{quota.ex => qouta/usage.ex} | 240 ++----------------
lib/plausible/billing/site_locker.ex | 2 +-
.../site/memberships/accept_invitation.ex | 4 +-
.../site/memberships/create_invitation.ex | 6 +-
lib/plausible/site/memberships/invitations.ex | 12 +-
.../controllers/admin_controller.ex | 8 +-
.../controllers/auth_controller.ex | 12 +-
.../controllers/site/membership_controller.ex | 4 +-
.../controllers/site_controller.ex | 8 +-
lib/plausible_web/live/choose_plan.ex | 44 ++--
lib/plausible_web/views/auth_view.ex | 2 +-
lib/workers/check_usage.ex | 62 ++---
lib/workers/send_trial_notifications.ex | 4 +-
test/plausible/billing/quota_test.exs | 144 +++++------
.../controllers/site_controller_test.exs | 2 +-
test/plausible_web/live/choose_plan_test.exs | 40 ++-
test/workers/check_usage_test.exs | 90 +++----
22 files changed, 486 insertions(+), 446 deletions(-)
create mode 100644 lib/plausible/billing/qouta/limits.ex
create mode 100644 lib/plausible/billing/qouta/quota.ex
rename lib/plausible/billing/{quota.ex => qouta/usage.ex} (54%)
diff --git a/lib/mix/tasks/pull_sandbox_subscription.ex b/lib/mix/tasks/pull_sandbox_subscription.ex
index d2824c869..438b36951 100644
--- a/lib/mix/tasks/pull_sandbox_subscription.ex
+++ b/lib/mix/tasks/pull_sandbox_subscription.ex
@@ -48,6 +48,7 @@ defmodule Mix.Tasks.PullSandboxSubscription do
update_url: res["update_url"],
user_id: user.id,
status: res["state"],
+ last_bill_date: res["last_payment"]["date"],
next_bill_date: res["next_payment"]["date"],
next_bill_amount: res["next_payment"]["amount"] |> to_string(),
currency_code: res["next_payment"]["currency"]
diff --git a/lib/plausible/billing/feature.ex b/lib/plausible/billing/feature.ex
index f6bed68bd..11aeb5d2e 100644
--- a/lib/plausible/billing/feature.ex
+++ b/lib/plausible/billing/feature.ex
@@ -132,7 +132,7 @@ defmodule Plausible.Billing.Feature do
def check_availability(%Plausible.Auth.User{} = user) do
cond do
free?() -> :ok
- __MODULE__ in Quota.allowed_features_for(user) -> :ok
+ __MODULE__ in Quota.Limits.allowed_features_for(user) -> :ok
true -> {:error, :upgrade_required}
end
end
diff --git a/lib/plausible/billing/plans.ex b/lib/plausible/billing/plans.ex
index 9462362b5..066f3cbf2 100644
--- a/lib/plausible/billing/plans.ex
+++ b/lib/plausible/billing/plans.ex
@@ -235,7 +235,7 @@ defmodule Plausible.Billing.Plans do
[]
end
- if Enum.any?(Quota.features_usage(user), &(&1 not in growth_features)) do
+ if Enum.any?(Quota.Usage.features_usage(user), &(&1 not in growth_features)) do
:business
else
:growth
diff --git a/lib/plausible/billing/qouta/limits.ex b/lib/plausible/billing/qouta/limits.ex
new file mode 100644
index 000000000..2dcedc622
--- /dev/null
+++ b/lib/plausible/billing/qouta/limits.ex
@@ -0,0 +1,120 @@
+defmodule Plausible.Billing.Quota.Limits do
+ @moduledoc false
+
+ use Plausible
+ alias Plausible.Users
+ alias Plausible.Auth.User
+ alias Plausible.Billing.{Plan, Plans, Subscription, EnterprisePlan, Feature}
+ alias Plausible.Billing.Feature.{Goals, Props, StatsAPI}
+
+ @type over_limits_error() :: {:over_plan_limits, [limit()]}
+ @typep limit() :: :site_limit | :pageview_limit | :team_member_limit
+ @pageview_allowance_margin 0.1
+
+ on_ee do
+ @limit_sites_since ~D[2021-05-05]
+ @site_limit_for_trials 10
+ @team_member_limit_for_trials 3
+
+ @spec site_limit(User.t()) :: non_neg_integer() | :unlimited
+ def site_limit(user) do
+ if Timex.before?(user.inserted_at, @limit_sites_since) do
+ :unlimited
+ else
+ get_site_limit_from_plan(user)
+ end
+ end
+
+ defp get_site_limit_from_plan(user) do
+ user = Users.with_subscription(user)
+
+ case Plans.get_subscription_plan(user.subscription) do
+ %{site_limit: site_limit} -> site_limit
+ :free_10k -> 50
+ nil -> @site_limit_for_trials
+ end
+ end
+
+ @spec team_member_limit(User.t()) :: non_neg_integer()
+ def team_member_limit(user) do
+ user = Users.with_subscription(user)
+
+ case Plans.get_subscription_plan(user.subscription) do
+ %{team_member_limit: limit} -> limit
+ :free_10k -> :unlimited
+ nil -> @team_member_limit_for_trials
+ end
+ end
+ else
+ def site_limit(_) do
+ :unlimited
+ end
+
+ def team_member_limit(_) do
+ :unlimited
+ end
+ end
+
+ @monthly_pageview_limit_for_free_10k 10_000
+ @monthly_pageview_limit_for_trials :unlimited
+
+ @spec monthly_pageview_limit(User.t() | Subscription.t()) ::
+ non_neg_integer() | :unlimited
+ def monthly_pageview_limit(%User{} = user) do
+ user = Users.with_subscription(user)
+ monthly_pageview_limit(user.subscription)
+ end
+
+ def monthly_pageview_limit(subscription) do
+ case Plans.get_subscription_plan(subscription) do
+ %EnterprisePlan{monthly_pageview_limit: limit} ->
+ limit
+
+ %Plan{monthly_pageview_limit: limit} ->
+ limit
+
+ :free_10k ->
+ @monthly_pageview_limit_for_free_10k
+
+ _any ->
+ if subscription do
+ Sentry.capture_message("Unknown monthly pageview limit for plan",
+ extra: %{paddle_plan_id: subscription.paddle_plan_id}
+ )
+ end
+
+ @monthly_pageview_limit_for_trials
+ end
+ end
+
+ def pageview_limit_with_margin(limit, margin \\ nil) do
+ margin = if margin, do: margin, else: @pageview_allowance_margin
+ ceil(limit * (1 + margin))
+ end
+
+ @doc """
+ Returns a list of features the user can use. Trial users have the
+ ability to use all features during their trial.
+ """
+ def allowed_features_for(user) do
+ user = Users.with_subscription(user)
+
+ case Plans.get_subscription_plan(user.subscription) do
+ %EnterprisePlan{features: features} ->
+ features
+
+ %Plan{features: features} ->
+ features
+
+ :free_10k ->
+ [Goals, Props, StatsAPI]
+
+ nil ->
+ if Users.on_trial?(user) do
+ Feature.list()
+ else
+ [Goals]
+ end
+ end
+ end
+end
diff --git a/lib/plausible/billing/qouta/quota.ex b/lib/plausible/billing/qouta/quota.ex
new file mode 100644
index 000000000..9ee576047
--- /dev/null
+++ b/lib/plausible/billing/qouta/quota.ex
@@ -0,0 +1,123 @@
+defmodule Plausible.Billing.Quota do
+ @moduledoc """
+ This module provides functions to work with plans usage and limits.
+ """
+
+ use Plausible
+ alias Plausible.Users
+ alias Plausible.Auth.User
+ alias Plausible.Billing.{Plan, Plans, EnterprisePlan}
+ alias Plausible.Billing.Quota.{Usage, Limits}
+
+ @doc """
+ Enterprise plans are always allowed to add more sites (even when
+ over limit) to avoid service disruption. Their usage is checked
+ in a background job instead (see `check_usage.ex`).
+ """
+ def ensure_can_add_new_site(user) do
+ user = Users.with_subscription(user)
+
+ case Plans.get_subscription_plan(user.subscription) do
+ %EnterprisePlan{} ->
+ :ok
+
+ _ ->
+ usage = Usage.site_usage(user)
+ limit = Limits.site_limit(user)
+
+ if below_limit?(usage, limit), do: :ok, else: {:error, {:over_limit, limit}}
+ end
+ end
+
+ @doc """
+ Ensures that the given user (or the usage map) is within the limits
+ of the given plan.
+
+ An `opts` argument can be passed with `ignore_pageview_limit: true`
+ which bypasses the pageview limit check and returns `:ok` as long as
+ the other limits are not exceeded.
+ """
+ @spec ensure_within_plan_limits(User.t() | map(), struct() | atom() | nil, Keyword.t()) ::
+ :ok | {:error, Limits.over_limits_error()}
+ def ensure_within_plan_limits(user_or_usage, plan, opts \\ [])
+
+ def ensure_within_plan_limits(%User{} = user, %plan_mod{} = plan, opts)
+ when plan_mod in [Plan, EnterprisePlan] do
+ ensure_within_plan_limits(Usage.usage(user), plan, opts)
+ end
+
+ def ensure_within_plan_limits(usage, %plan_mod{} = plan, opts)
+ when plan_mod in [Plan, EnterprisePlan] do
+ case exceeded_limits(usage, plan, opts) do
+ [] -> :ok
+ exceeded_limits -> {:error, {:over_plan_limits, exceeded_limits}}
+ end
+ end
+
+ def ensure_within_plan_limits(_, _, _), do: :ok
+
+ defp exceeded_limits(usage, plan, opts) do
+ for {limit, exceeded?} <- [
+ {:team_member_limit, not within_limit?(usage.team_members, plan.team_member_limit)},
+ {:site_limit, not within_limit?(usage.sites, plan.site_limit)},
+ {:monthly_pageview_limit,
+ exceeds_monthly_pageview_limit?(usage.monthly_pageviews, plan, opts)}
+ ],
+ exceeded? do
+ limit
+ end
+ end
+
+ defp exceeds_monthly_pageview_limit?(usage, plan, opts) do
+ if Keyword.get(opts, :ignore_pageview_limit) do
+ false
+ else
+ case usage do
+ %{last_30_days: %{total: total}} ->
+ margin = Keyword.get(opts, :pageview_allowance_margin)
+ limit = Limits.pageview_limit_with_margin(plan.monthly_pageview_limit, margin)
+ !within_limit?(total, limit)
+
+ cycles_usage ->
+ exceeds_last_two_usage_cycles?(cycles_usage, plan.monthly_pageview_limit)
+ end
+ end
+ end
+
+ @spec exceeds_last_two_usage_cycles?(Usage.cycles_usage(), non_neg_integer()) :: boolean()
+ def exceeds_last_two_usage_cycles?(cycles_usage, allowed_volume) do
+ exceeded = exceeded_cycles(cycles_usage, allowed_volume)
+ :penultimate_cycle in exceeded && :last_cycle in exceeded
+ end
+
+ @spec exceeded_cycles(Usage.cycles_usage(), non_neg_integer()) :: list()
+ def exceeded_cycles(cycles_usage, allowed_volume) do
+ limit = Limits.pageview_limit_with_margin(allowed_volume)
+
+ Enum.reduce(cycles_usage, [], fn {cycle, %{total: total}}, exceeded_cycles ->
+ if below_limit?(total, limit) do
+ exceeded_cycles
+ else
+ exceeded_cycles ++ [cycle]
+ end
+ end)
+ end
+
+ @spec below_limit?(non_neg_integer(), non_neg_integer() | :unlimited) :: boolean()
+ @doc """
+ Returns whether the usage is below the limit or not.
+ Returns false if usage is equal to the limit.
+ """
+ def below_limit?(usage, limit) do
+ if limit == :unlimited, do: true, else: usage < limit
+ end
+
+ @spec within_limit?(non_neg_integer(), non_neg_integer() | :unlimited) :: boolean()
+ @doc """
+ Returns whether the usage is within the limit or not.
+ Returns true if usage is equal to the limit.
+ """
+ def within_limit?(usage, limit) do
+ if limit == :unlimited, do: true, else: usage <= limit
+ end
+end
diff --git a/lib/plausible/billing/quota.ex b/lib/plausible/billing/qouta/usage.ex
similarity index 54%
rename from lib/plausible/billing/quota.ex
rename to lib/plausible/billing/qouta/usage.ex
index a6505e5df..c0bfae7c9 100644
--- a/lib/plausible/billing/quota.ex
+++ b/lib/plausible/billing/qouta/usage.ex
@@ -1,34 +1,26 @@
-defmodule Plausible.Billing.Quota do
- @moduledoc """
- This module provides functions to work with plans usage and limits.
- """
+defmodule Plausible.Billing.Quota.Usage do
+ @moduledoc false
use Plausible
import Ecto.Query
alias Plausible.Users
alias Plausible.Auth.User
alias Plausible.Site
- alias Plausible.Billing.{Plan, Plans, Subscription, Subscriptions, EnterprisePlan, Feature}
- alias Plausible.Billing.Feature.{Goals, RevenueGoals, Funnels, Props, StatsAPI}
+ alias Plausible.Billing.{Subscriptions}
+ alias Plausible.Billing.Feature.{RevenueGoals, Funnels, Props, StatsAPI}
- @type limit() :: :site_limit | :pageview_limit | :team_member_limit
+ @type cycles_usage() :: %{cycle() => usage_cycle()}
- @type over_limits_error() :: {:over_plan_limits, [limit()]}
+ @typep cycle :: :current_cycle | :last_cycle | :penultimate_cycle
+ @typep last_30_days_usage() :: %{:last_30_days => usage_cycle()}
+ @typep monthly_pageview_usage() :: cycles_usage() | last_30_days_usage()
- @type monthly_pageview_usage() :: %{period() => usage_cycle()}
-
- @type period :: :last_30_days | :current_cycle | :last_cycle | :penultimate_cycle
-
- @type usage_cycle :: %{
- date_range: Date.Range.t(),
- pageviews: non_neg_integer(),
- custom_events: non_neg_integer(),
- total: non_neg_integer()
- }
-
- @pageview_allowance_margin 0.1
-
- def pageview_allowance_margin(), do: @pageview_allowance_margin
+ @typep usage_cycle :: %{
+ date_range: Date.Range.t(),
+ pageviews: non_neg_integer(),
+ custom_events: non_neg_integer(),
+ total: non_neg_integer()
+ }
def usage(user, opts \\ []) do
basic_usage = %{
@@ -45,50 +37,6 @@ defmodule Plausible.Billing.Quota do
end
end
- on_ee do
- @limit_sites_since ~D[2021-05-05]
- @site_limit_for_trials 10
- @team_member_limit_for_trials 3
-
- @spec site_limit(User.t()) :: non_neg_integer() | :unlimited
- def site_limit(user) do
- if Timex.before?(user.inserted_at, @limit_sites_since) do
- :unlimited
- else
- get_site_limit_from_plan(user)
- end
- end
-
- defp get_site_limit_from_plan(user) do
- user = Users.with_subscription(user)
-
- case Plans.get_subscription_plan(user.subscription) do
- %{site_limit: site_limit} -> site_limit
- :free_10k -> 50
- nil -> @site_limit_for_trials
- end
- end
-
- @spec team_member_limit(User.t()) :: non_neg_integer()
- def team_member_limit(user) do
- user = Users.with_subscription(user)
-
- case Plans.get_subscription_plan(user.subscription) do
- %{team_member_limit: limit} -> limit
- :free_10k -> :unlimited
- nil -> @team_member_limit_for_trials
- end
- end
- else
- def site_limit(_) do
- :unlimited
- end
-
- def team_member_limit(_) do
- :unlimited
- end
- end
-
@spec site_usage(User.t()) :: non_neg_integer()
@doc """
Returns the number of sites the given user owns.
@@ -97,58 +45,6 @@ defmodule Plausible.Billing.Quota do
Plausible.Sites.owned_sites_count(user)
end
- @doc """
- Enterprise plans are always allowed to add more sites (even when
- over limit) to avoid service disruption. Their usage is checked
- in a background job instead (see `check_usage.ex`).
- """
- def ensure_can_add_new_site(user) do
- user = Users.with_subscription(user)
-
- case Plans.get_subscription_plan(user.subscription) do
- %EnterprisePlan{} ->
- :ok
-
- _ ->
- usage = site_usage(user)
- limit = site_limit(user)
-
- if below_limit?(usage, limit), do: :ok, else: {:error, {:over_limit, limit}}
- end
- end
-
- @monthly_pageview_limit_for_free_10k 10_000
- @monthly_pageview_limit_for_trials :unlimited
-
- @spec monthly_pageview_limit(User.t() | Subscription.t()) ::
- non_neg_integer() | :unlimited
- def monthly_pageview_limit(%User{} = user) do
- user = Users.with_subscription(user)
- monthly_pageview_limit(user.subscription)
- end
-
- def monthly_pageview_limit(subscription) do
- case Plans.get_subscription_plan(subscription) do
- %EnterprisePlan{monthly_pageview_limit: limit} ->
- limit
-
- %Plan{monthly_pageview_limit: limit} ->
- limit
-
- :free_10k ->
- @monthly_pageview_limit_for_free_10k
-
- _any ->
- if subscription do
- Sentry.capture_message("Unknown monthly pageview limit for plan",
- extra: %{paddle_plan_id: subscription.paddle_plan_id}
- )
- end
-
- @monthly_pageview_limit_for_trials
- end
- end
-
@doc """
Queries the ClickHouse database for the monthly pageview usage. If the given user's
subscription is `active`, `past_due`, or a `deleted` (but not yet expired), a map
@@ -195,8 +91,7 @@ defmodule Plausible.Billing.Quota do
end
end
- @spec usage_cycle(User.t(), period(), list() | nil, Date.t()) :: usage_cycle()
-
+ @spec usage_cycle(User.t(), :last_30_days | cycle(), list() | nil, Date.t()) :: usage_cycle()
def usage_cycle(user, cycle, owned_site_ids \\ nil, today \\ Timex.today())
def usage_cycle(user, cycle, nil, today) do
@@ -398,114 +293,9 @@ defmodule Plausible.Billing.Quota do
for {f_mod, used?} <- used_features, used?, f_mod.enabled?(site), do: f_mod
end
- @doc """
- Ensures that the given user (or the usage map) is within the limits
- of the given plan.
-
- An `opts` argument can be passed with `ignore_pageview_limit: true`
- which bypasses the pageview limit check and returns `:ok` as long as
- the other limits are not exceeded.
- """
- @spec ensure_within_plan_limits(User.t() | map(), struct() | atom() | nil, Keyword.t()) ::
- :ok | {:error, over_limits_error()}
- def ensure_within_plan_limits(user_or_usage, plan, opts \\ [])
-
- def ensure_within_plan_limits(%User{} = user, %plan_mod{} = plan, opts)
- when plan_mod in [Plan, EnterprisePlan] do
- ensure_within_plan_limits(usage(user), plan, opts)
- end
-
- def ensure_within_plan_limits(usage, %plan_mod{} = plan, opts)
- when plan_mod in [Plan, EnterprisePlan] do
- case exceeded_limits(usage, plan, opts) do
- [] -> :ok
- exceeded_limits -> {:error, {:over_plan_limits, exceeded_limits}}
- end
- end
-
- def ensure_within_plan_limits(_, _, _), do: :ok
-
- defp exceeded_limits(usage, plan, opts) do
- for {limit, exceeded?} <- [
- {:team_member_limit, not within_limit?(usage.team_members, plan.team_member_limit)},
- {:site_limit, not within_limit?(usage.sites, plan.site_limit)},
- {:monthly_pageview_limit,
- exceeds_monthly_pageview_limit?(usage.monthly_pageviews, plan, opts)}
- ],
- exceeded? do
- limit
- end
- end
-
- defp exceeds_monthly_pageview_limit?(usage, plan, opts) do
- if Keyword.get(opts, :ignore_pageview_limit) do
- false
- else
- case usage do
- %{last_30_days: %{total: total}} ->
- !within_limit?(total, pageview_limit_with_margin(plan, opts))
-
- billing_cycles_usage ->
- Plausible.Workers.CheckUsage.exceeds_last_two_usage_cycles?(
- billing_cycles_usage,
- plan.monthly_pageview_limit
- )
- end
- end
- end
-
- defp pageview_limit_with_margin(%{monthly_pageview_limit: limit}, opts) do
- margin = Keyword.get(opts, :pageview_allowance_margin, @pageview_allowance_margin)
- ceil(limit * (1 + margin))
- end
-
- @doc """
- Returns a list of features the user can use. Trial users have the
- ability to use all features during their trial.
- """
- def allowed_features_for(user) do
- user = Users.with_subscription(user)
-
- case Plans.get_subscription_plan(user.subscription) do
- %EnterprisePlan{features: features} ->
- features
-
- %Plan{features: features} ->
- features
-
- :free_10k ->
- [Goals, Props, StatsAPI]
-
- nil ->
- if Users.on_trial?(user) do
- Feature.list()
- else
- [Goals]
- end
- end
- end
-
defp owned_sites_query(user) do
from sm in Site.Membership,
where: sm.role == :owner and sm.user_id == ^user.id,
select: %{site_id: sm.site_id}
end
-
- @spec below_limit?(non_neg_integer(), non_neg_integer() | :unlimited) :: boolean()
- @doc """
- Returns whether the usage is below the limit or not.
- Returns false if usage is equal to the limit.
- """
- def below_limit?(usage, limit) do
- if limit == :unlimited, do: true, else: usage < limit
- end
-
- @spec within_limit?(non_neg_integer(), non_neg_integer() | :unlimited) :: boolean()
- @doc """
- Returns whether the usage is within the limit or not.
- Returns true if usage is equal to the limit.
- """
- def within_limit?(usage, limit) do
- if limit == :unlimited, do: true, else: usage <= limit
- end
end
diff --git a/lib/plausible/billing/site_locker.ex b/lib/plausible/billing/site_locker.ex
index 67ba076b1..11b2c4228 100644
--- a/lib/plausible/billing/site_locker.ex
+++ b/lib/plausible/billing/site_locker.ex
@@ -68,7 +68,7 @@ defmodule Plausible.Billing.SiteLocker do
@spec send_grace_period_end_email(Plausible.Auth.User.t()) :: Plausible.Mailer.result()
def send_grace_period_end_email(user) do
- usage = Plausible.Billing.Quota.monthly_pageview_usage(user)
+ usage = Plausible.Billing.Quota.Usage.monthly_pageview_usage(user)
suggested_plan = Plausible.Billing.Plans.suggest(user, usage.last_cycle.total)
PlausibleWeb.Email.dashboard_locked(user, usage, suggested_plan)
diff --git a/lib/plausible/site/memberships/accept_invitation.ex b/lib/plausible/site/memberships/accept_invitation.ex
index bffe63105..98f82d3dd 100644
--- a/lib/plausible/site/memberships/accept_invitation.ex
+++ b/lib/plausible/site/memberships/accept_invitation.ex
@@ -26,7 +26,7 @@ defmodule Plausible.Site.Memberships.AcceptInvitation do
@spec transfer_ownership(Site.t(), Auth.User.t()) ::
{:ok, Site.Membership.t()}
| {:error,
- Billing.Quota.over_limits_error()
+ Billing.Quota.Limits.over_limits_error()
| Ecto.Changeset.t()
| :transfer_to_self
| :no_plan}
@@ -54,7 +54,7 @@ defmodule Plausible.Site.Memberships.AcceptInvitation do
{:ok, Site.Membership.t()}
| {:error,
:invitation_not_found
- | Billing.Quota.over_limits_error()
+ | Billing.Quota.Limits.over_limits_error()
| Ecto.Changeset.t()
| :no_plan}
def accept_invitation(invitation_id, user) do
diff --git a/lib/plausible/site/memberships/create_invitation.ex b/lib/plausible/site/memberships/create_invitation.ex
index 4961f8f68..689ec8e2e 100644
--- a/lib/plausible/site/memberships/create_invitation.ex
+++ b/lib/plausible/site/memberships/create_invitation.ex
@@ -41,7 +41,7 @@ defmodule Plausible.Site.Memberships.CreateInvitation do
{:ok, [Membership.t()]}
| {:error,
invite_error()
- | Quota.over_limits_error()}
+ | Quota.Limits.over_limits_error()}
def bulk_transfer_ownership_direct(sites, new_owner) do
Plausible.Repo.transaction(fn ->
for site <- sites do
@@ -134,8 +134,8 @@ defmodule Plausible.Site.Memberships.CreateInvitation do
defp check_team_member_limit(site, _role, invitee_email) do
site = Plausible.Repo.preload(site, :owner)
- limit = Quota.team_member_limit(site.owner)
- usage = Quota.team_member_usage(site.owner, exclude_emails: invitee_email)
+ limit = Quota.Limits.team_member_limit(site.owner)
+ usage = Quota.Usage.team_member_usage(site.owner, exclude_emails: invitee_email)
if Quota.below_limit?(usage, limit),
do: :ok,
diff --git a/lib/plausible/site/memberships/invitations.ex b/lib/plausible/site/memberships/invitations.ex
index c8569aad3..11d59fb52 100644
--- a/lib/plausible/site/memberships/invitations.ex
+++ b/lib/plausible/site/memberships/invitations.ex
@@ -66,7 +66,7 @@ defmodule Plausible.Site.Memberships.Invitations do
on_ee do
@spec ensure_can_take_ownership(Site.t(), Auth.User.t()) ::
- :ok | {:error, Quota.over_limits_error() | :no_plan}
+ :ok | {:error, Quota.Limits.over_limits_error() | :no_plan}
def ensure_can_take_ownership(site, new_owner) do
site = Repo.preload(site, :owner)
new_owner = Plausible.Users.with_subscription(new_owner)
@@ -78,7 +78,7 @@ defmodule Plausible.Site.Memberships.Invitations do
usage_after_transfer = %{
monthly_pageviews: monthly_pageview_usage_after_transfer(site, new_owner),
team_members: team_member_usage_after_transfer(site, new_owner),
- sites: Quota.site_usage(new_owner) + 1
+ sites: Quota.Usage.site_usage(new_owner) + 1
}
Quota.ensure_within_plan_limits(usage_after_transfer, plan)
@@ -88,8 +88,8 @@ defmodule Plausible.Site.Memberships.Invitations do
end
defp team_member_usage_after_transfer(site, new_owner) do
- current_usage = Quota.team_member_usage(new_owner)
- site_usage = Quota.team_member_usage(site.owner, site: site)
+ current_usage = Quota.Usage.team_member_usage(new_owner)
+ site_usage = Quota.Usage.team_member_usage(site.owner, site: site)
extra_usage =
if Plausible.Sites.is_member?(new_owner.id, site), do: 0, else: 1
@@ -99,7 +99,7 @@ defmodule Plausible.Site.Memberships.Invitations do
defp monthly_pageview_usage_after_transfer(site, new_owner) do
site_ids = Plausible.Sites.owned_site_ids(new_owner) ++ [site.id]
- Quota.monthly_pageview_usage(new_owner, site_ids)
+ Quota.Usage.monthly_pageview_usage(new_owner, site_ids)
end
else
@spec ensure_can_take_ownership(Site.t(), Auth.User.t()) :: :ok
@@ -117,7 +117,7 @@ defmodule Plausible.Site.Memberships.Invitations do
def check_feature_access(site, new_owner, false = _selfhost?) do
missing_features =
site
- |> Quota.features_usage()
+ |> Quota.Usage.features_usage()
|> Enum.filter(&(&1.check_availability(new_owner) != :ok))
if missing_features == [] do
diff --git a/lib/plausible_web/controllers/admin_controller.ex b/lib/plausible_web/controllers/admin_controller.ex
index d10bab1a5..a10fd7bf2 100644
--- a/lib/plausible_web/controllers/admin_controller.ex
+++ b/lib/plausible_web/controllers/admin_controller.ex
@@ -9,12 +9,12 @@ defmodule PlausibleWeb.AdminController do
|> String.to_integer()
|> Plausible.Users.with_subscription()
- usage = Quota.usage(user, with_features: true)
+ usage = Quota.Usage.usage(user, with_features: true)
limits = %{
- monthly_pageviews: Quota.monthly_pageview_limit(user),
- sites: Quota.site_limit(user),
- team_members: Quota.team_member_limit(user)
+ monthly_pageviews: Quota.Limits.monthly_pageview_limit(user),
+ sites: Quota.Limits.site_limit(user),
+ team_members: Quota.Limits.team_member_limit(user)
}
html_response = usage_and_limits_html(user, usage, limits)
diff --git a/lib/plausible_web/controllers/auth_controller.ex b/lib/plausible_web/controllers/auth_controller.ex
index 1a526c09e..c9eff5765 100644
--- a/lib/plausible_web/controllers/auth_controller.ex
+++ b/lib/plausible_web/controllers/auth_controller.ex
@@ -633,12 +633,12 @@ defmodule PlausibleWeb.AuthController do
subscription: user.subscription,
invoices: Plausible.Billing.paddle_api().get_invoices(user.subscription),
theme: user.theme || "system",
- team_member_limit: Quota.team_member_limit(user),
- team_member_usage: Quota.team_member_usage(user),
- site_limit: Quota.site_limit(user),
- site_usage: Quota.site_usage(user),
- pageview_limit: Quota.monthly_pageview_limit(user),
- pageview_usage: Quota.monthly_pageview_usage(user),
+ team_member_limit: Quota.Limits.team_member_limit(user),
+ team_member_usage: Quota.Usage.team_member_usage(user),
+ site_limit: Quota.Limits.site_limit(user),
+ site_usage: Quota.Usage.site_usage(user),
+ pageview_limit: Quota.Limits.monthly_pageview_limit(user),
+ pageview_usage: Quota.Usage.monthly_pageview_usage(user),
totp_enabled?: Auth.TOTP.enabled?(user)
)
end
diff --git a/lib/plausible_web/controllers/site/membership_controller.ex b/lib/plausible_web/controllers/site/membership_controller.ex
index 65361f3d8..8cd5101d8 100644
--- a/lib/plausible_web/controllers/site/membership_controller.ex
+++ b/lib/plausible_web/controllers/site/membership_controller.ex
@@ -29,8 +29,8 @@ defmodule PlausibleWeb.Site.MembershipController do
|> Sites.get_for_user!(conn.assigns.site.domain)
|> Plausible.Repo.preload(:owner)
- limit = Plausible.Billing.Quota.team_member_limit(site.owner)
- usage = Plausible.Billing.Quota.team_member_usage(site.owner)
+ limit = Plausible.Billing.Quota.Limits.team_member_limit(site.owner)
+ usage = Plausible.Billing.Quota.Usage.team_member_usage(site.owner)
below_limit? = Plausible.Billing.Quota.below_limit?(usage, limit)
render(
diff --git a/lib/plausible_web/controllers/site_controller.ex b/lib/plausible_web/controllers/site_controller.ex
index 887c68d0e..6082151a8 100644
--- a/lib/plausible_web/controllers/site_controller.ex
+++ b/lib/plausible_web/controllers/site_controller.ex
@@ -18,8 +18,8 @@ defmodule PlausibleWeb.SiteController do
render(conn, "new.html",
changeset: Plausible.Site.changeset(%Plausible.Site{}),
- first_site?: Quota.site_usage(current_user) == 0,
- site_limit: Quota.site_limit(current_user),
+ first_site?: Quota.Usage.site_usage(current_user) == 0,
+ site_limit: Quota.Limits.site_limit(current_user),
site_limit_exceeded?: Quota.ensure_can_add_new_site(current_user) != :ok,
layout: {PlausibleWeb.LayoutView, "focus.html"}
)
@@ -27,7 +27,7 @@ defmodule PlausibleWeb.SiteController do
def create_site(conn, %{"site" => site_params}) do
user = conn.assigns[:current_user]
- first_site? = Quota.site_usage(user) == 0
+ first_site? = Quota.Usage.site_usage(user) == 0
case Sites.create(user, site_params) do
{:ok, %{site: site}} ->
@@ -53,7 +53,7 @@ defmodule PlausibleWeb.SiteController do
render(conn, "new.html",
changeset: changeset,
first_site?: first_site?,
- site_limit: Quota.site_limit(user),
+ site_limit: Quota.Limits.site_limit(user),
site_limit_exceeded?: false,
layout: {PlausibleWeb.LayoutView, "focus.html"}
)
diff --git a/lib/plausible_web/live/choose_plan.ex b/lib/plausible_web/live/choose_plan.ex
index 5819b352a..7b9ca0989 100644
--- a/lib/plausible_web/live/choose_plan.ex
+++ b/lib/plausible_web/live/choose_plan.ex
@@ -10,7 +10,7 @@ defmodule PlausibleWeb.Live.ChoosePlan do
alias PlausibleWeb.Components.Billing.{PlanBox, PlanBenefits, Notice, PageviewSlider}
alias Plausible.Site
alias Plausible.Users
- alias Plausible.Billing.{Plans, Plan, Quota}
+ alias Plausible.Billing.{Plans, Quota}
@contact_link "https://plausible.io/contact"
@billing_faq_link "https://plausible.io/docs/billing"
@@ -22,13 +22,7 @@ defmodule PlausibleWeb.Live.ChoosePlan do
Users.with_subscription(user_id)
end)
|> assign_new(:usage, fn %{user: user} ->
- Quota.usage(user, with_features: true)
- end)
- |> assign_new(:last_30_days_usage, fn %{user: user, usage: usage} ->
- case usage do
- %{last_30_days: usage_cycle} -> usage_cycle.total
- _ -> Quota.usage_cycle(user, :last_30_days).total
- end
+ Quota.Usage.usage(user, with_features: true)
end)
|> assign_new(:owned_plan, fn %{user: %{subscription: subscription}} ->
Plans.get_regular_plan(subscription, only_non_expired: true)
@@ -63,11 +57,10 @@ defmodule PlausibleWeb.Live.ChoosePlan do
get_available_volumes(available_plans)
end)
|> assign_new(:selected_volume, fn %{
- owned_plan: owned_plan,
- last_30_days_usage: last_30_days_usage,
+ usage: usage,
available_volumes: available_volumes
} ->
- default_selected_volume(owned_plan, last_30_days_usage, available_volumes)
+ default_selected_volume(usage.monthly_pageviews, available_volumes)
end)
|> assign_new(:selected_interval, fn %{current_interval: current_interval} ->
current_interval || :monthly
@@ -149,8 +142,7 @@ defmodule PlausibleWeb.Live.ChoosePlan do
- You have used <%= PlausibleWeb.AuthView.delimit_integer(@last_30_days_usage) %> - billable pageviews in the last 30 days + <.render_usage pageview_usage={@usage.monthly_pageviews} />
<.pageview_limit_notice :if={!@owned_plan} /> <.help_links /> @@ -160,6 +152,22 @@ defmodule PlausibleWeb.Live.ChoosePlan do """ end + defp render_usage(assigns) do + case assigns.pageview_usage do + %{last_30_days: _} -> + ~H""" + You have used + <%= PlausibleWeb.AuthView.delimit_integer(@pageview_usage.last_30_days.total) %> billable pageviews in the last 30 days + """ + + %{last_cycle: _} -> + ~H""" + You have used + <%= PlausibleWeb.AuthView.delimit_integer(@pageview_usage.last_cycle.total) %> billable pageviews in the last billing cycle + """ + end + end + def handle_event("set_interval", %{"interval" => interval}, socket) do new_interval = case interval do @@ -189,10 +197,14 @@ defmodule PlausibleWeb.Live.ChoosePlan do )} end - defp default_selected_volume(%Plan{monthly_pageview_limit: limit}, _, _), do: limit + defp default_selected_volume(pageview_usage, available_volumes) do + total = + case pageview_usage do + %{last_30_days: usage} -> usage.total + %{last_cycle: usage} -> usage.total + end - defp default_selected_volume(_, last_30_days_usage, available_volumes) do - Enum.find(available_volumes, &(last_30_days_usage < &1)) || :enterprise + Enum.find(available_volumes, &(total < &1)) || :enterprise end defp current_user_subscription_interval(subscription) do diff --git a/lib/plausible_web/views/auth_view.ex b/lib/plausible_web/views/auth_view.ex index a3c6d1972..31f9fd8f6 100644 --- a/lib/plausible_web/views/auth_view.ex +++ b/lib/plausible_web/views/auth_view.ex @@ -10,7 +10,7 @@ defmodule PlausibleWeb.AuthView do def subscription_quota(subscription, options) do subscription - |> Plausible.Billing.Quota.monthly_pageview_limit() + |> Plausible.Billing.Quota.Limits.monthly_pageview_limit() |> PlausibleWeb.StatsView.large_number_format() |> then(fn quota -> if Keyword.get(options, :format) == :long, diff --git a/lib/workers/check_usage.ex b/lib/workers/check_usage.ex index d19f382e1..6a3a0b4fc 100644 --- a/lib/workers/check_usage.ex +++ b/lib/workers/check_usage.ex @@ -33,7 +33,7 @@ defmodule Plausible.Workers.CheckUsage do end @impl Oban.Worker - def perform(_job, quota_mod \\ Quota, today \\ Timex.today()) do + def perform(_job, usage_mod \\ Quota.Usage, today \\ Timex.today()) do yesterday = today |> Timex.shift(days: -1) active_subscribers = @@ -56,13 +56,13 @@ defmodule Plausible.Workers.CheckUsage do for subscriber <- active_subscribers do case {subscriber.grace_period, subscriber.enterprise_plan} do {nil, nil} -> - check_regular_subscriber(subscriber, quota_mod) + check_regular_subscriber(subscriber, usage_mod) {nil, _} -> - check_enterprise_subscriber(subscriber, quota_mod) + check_enterprise_subscriber(subscriber, usage_mod) {_, nil} -> - maybe_remove_grace_period(subscriber, quota_mod) + maybe_remove_grace_period(subscriber, usage_mod) _ -> :skip @@ -72,29 +72,9 @@ defmodule Plausible.Workers.CheckUsage do :ok end - @spec exceeds_last_two_usage_cycles?(Quota.monthly_pageview_usage(), non_neg_integer()) :: - boolean() - - def exceeds_last_two_usage_cycles?(usage, limit) when is_integer(limit) do - limit = ceil(limit * (1 + Quota.pageview_allowance_margin())) - - Enum.all?([usage.last_cycle, usage.penultimate_cycle], fn usage -> - not Quota.below_limit?(usage.total, limit) - end) - end - - @spec last_usage_cycle_below_limit?(Quota.monthly_pageview_usage(), non_neg_integer()) :: - boolean() - - def last_usage_cycle_below_limit?(usage, limit) when is_integer(limit) do - limit = ceil(limit * (1 + Quota.pageview_allowance_margin())) - - Quota.below_limit?(usage.last_cycle.total, limit) - end - defp check_site_usage_for_enterprise(subscriber) do limit = subscriber.enterprise_plan.site_limit - usage = Quota.site_usage(subscriber) + usage = Quota.Usage.site_usage(subscriber) if Quota.below_limit?(usage, limit) do {:below_limit, {usage, limit}} @@ -103,8 +83,8 @@ defmodule Plausible.Workers.CheckUsage do end end - def maybe_remove_grace_period(subscriber, quota_mod) do - case check_pageview_usage_last_cycle(subscriber, quota_mod) do + def maybe_remove_grace_period(subscriber, usage_mod) do + case check_pageview_usage_last_cycle(subscriber, usage_mod) do {:below_limit, _} -> subscriber |> Plausible.Auth.GracePeriod.remove_changeset() @@ -115,8 +95,8 @@ defmodule Plausible.Workers.CheckUsage do end end - defp check_regular_subscriber(subscriber, quota_mod) do - case check_pageview_usage_two_cycles(subscriber, quota_mod) do + defp check_regular_subscriber(subscriber, usage_mod) do + case check_pageview_usage_two_cycles(subscriber, usage_mod) do {:over_limit, pageview_usage} -> suggested_plan = Plausible.Billing.Plans.suggest(subscriber, pageview_usage.last_cycle.total) @@ -133,8 +113,8 @@ defmodule Plausible.Workers.CheckUsage do end end - def check_enterprise_subscriber(subscriber, quota_mod) do - pageview_usage = check_pageview_usage_two_cycles(subscriber, quota_mod) + def check_enterprise_subscriber(subscriber, usage_mod) do + pageview_usage = check_pageview_usage_two_cycles(subscriber, usage_mod) site_usage = check_site_usage_for_enterprise(subscriber) case {pageview_usage, site_usage} do @@ -156,25 +136,25 @@ defmodule Plausible.Workers.CheckUsage do end end - defp check_pageview_usage_two_cycles(subscriber, quota_mod) do - usage = quota_mod.monthly_pageview_usage(subscriber) - limit = Quota.monthly_pageview_limit(subscriber) + defp check_pageview_usage_two_cycles(subscriber, usage_mod) do + usage = usage_mod.monthly_pageview_usage(subscriber) + limit = Quota.Limits.monthly_pageview_limit(subscriber) - if exceeds_last_two_usage_cycles?(usage, limit) do + if Quota.exceeds_last_two_usage_cycles?(usage, limit) do {:over_limit, usage} else {:below_limit, usage} end end - defp check_pageview_usage_last_cycle(subscriber, quota_mod) do - usage = quota_mod.monthly_pageview_usage(subscriber) - limit = Quota.monthly_pageview_limit(subscriber) + defp check_pageview_usage_last_cycle(subscriber, usage_mod) do + usage = usage_mod.monthly_pageview_usage(subscriber) + limit = Quota.Limits.monthly_pageview_limit(subscriber) - if last_usage_cycle_below_limit?(usage, limit) do - {:below_limit, usage} - else + if :last_cycle in Quota.exceeded_cycles(usage, limit) do {:over_limit, usage} + else + {:below_limit, usage} end end end diff --git a/lib/workers/send_trial_notifications.ex b/lib/workers/send_trial_notifications.ex index 80fd3f8da..c71747b9a 100644 --- a/lib/workers/send_trial_notifications.ex +++ b/lib/workers/send_trial_notifications.ex @@ -55,14 +55,14 @@ defmodule Plausible.Workers.SendTrialNotifications do end defp send_tomorrow_reminder(user) do - usage = Plausible.Billing.Quota.usage_cycle(user, :last_30_days) + usage = Plausible.Billing.Quota.Usage.usage_cycle(user, :last_30_days) PlausibleWeb.Email.trial_upgrade_email(user, "tomorrow", usage) |> Plausible.Mailer.send() end defp send_today_reminder(user) do - usage = Plausible.Billing.Quota.usage_cycle(user, :last_30_days) + usage = Plausible.Billing.Quota.Usage.usage_cycle(user, :last_30_days) PlausibleWeb.Email.trial_upgrade_email(user, "today", usage) |> Plausible.Mailer.send() diff --git a/test/plausible/billing/quota_test.exs b/test/plausible/billing/quota_test.exs index 0bc76baeb..f7ed49caf 100644 --- a/test/plausible/billing/quota_test.exs +++ b/test/plausible/billing/quota_test.exs @@ -24,14 +24,14 @@ defmodule Plausible.Billing.QuotaTest do user_on_v2 = insert(:user, subscription: build(:subscription, paddle_plan_id: @v2_plan_id)) user_on_v3 = insert(:user, subscription: build(:subscription, paddle_plan_id: @v3_plan_id)) - assert 50 == Quota.site_limit(user_on_v1) - assert 50 == Quota.site_limit(user_on_v2) - assert 50 == Quota.site_limit(user_on_v3) + assert 50 == Quota.Limits.site_limit(user_on_v1) + assert 50 == Quota.Limits.site_limit(user_on_v2) + assert 50 == Quota.Limits.site_limit(user_on_v3) end test "returns 50 when user is on free_10k plan" do user = insert(:user, subscription: build(:subscription, paddle_plan_id: "free_10k")) - assert 50 == Quota.site_limit(user) + assert 50 == Quota.Limits.site_limit(user) end test "returns the configured site limit for enterprise plan" do @@ -40,7 +40,7 @@ defmodule Plausible.Billing.QuotaTest do enterprise_plan = insert(:enterprise_plan, user_id: user.id, site_limit: 500) insert(:subscription, user_id: user.id, paddle_plan_id: enterprise_plan.paddle_plan_id) - assert enterprise_plan.site_limit == Quota.site_limit(user) + assert enterprise_plan.site_limit == Quota.Limits.site_limit(user) end test "returns 10 when user in on trial" do @@ -49,7 +49,7 @@ defmodule Plausible.Billing.QuotaTest do trial_expiry_date: Timex.shift(Timex.now(), days: 7) ) - assert 10 == Quota.site_limit(user) + assert 10 == Quota.Limits.site_limit(user) end test "returns the subscription limit for enterprise users who have not paid yet" do @@ -59,7 +59,7 @@ defmodule Plausible.Billing.QuotaTest do subscription: build(:subscription, paddle_plan_id: @v1_plan_id) ) - assert 50 == Quota.site_limit(user) + assert 50 == Quota.Limits.site_limit(user) end test "returns 10 for enterprise users who have not upgraded yet and are on trial" do @@ -69,7 +69,7 @@ defmodule Plausible.Billing.QuotaTest do subscription: nil ) - assert 10 == Quota.site_limit(user) + assert 10 == Quota.Limits.site_limit(user) end end @@ -79,7 +79,7 @@ defmodule Plausible.Billing.QuotaTest do insert(:site, memberships: [build(:site_membership, user: user, role: :admin)]) insert(:site, memberships: [build(:site_membership, user: user, role: :viewer)]) - assert Quota.site_usage(user) == 3 + assert Quota.Usage.site_usage(user) == 3 end describe "below_limit?/2" do @@ -209,19 +209,19 @@ defmodule Plausible.Billing.QuotaTest do test "is based on the plan if user is on a legacy plan" do user = insert(:user, subscription: build(:subscription, paddle_plan_id: @legacy_plan_id)) - assert Quota.monthly_pageview_limit(user.subscription) == 1_000_000 + assert Quota.Limits.monthly_pageview_limit(user.subscription) == 1_000_000 end 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)) - assert Quota.monthly_pageview_limit(user.subscription) == 10_000 + assert Quota.Limits.monthly_pageview_limit(user.subscription) == 10_000 end test "free_10k has 10k monthly_pageview_limit" do user = insert(:user, subscription: build(:subscription, paddle_plan_id: "free_10k")) - assert Quota.monthly_pageview_limit(user.subscription) == 10_000 + assert Quota.Limits.monthly_pageview_limit(user.subscription) == 10_000 end test "is based on the enterprise plan if user is on an enterprise plan" do @@ -233,14 +233,14 @@ defmodule Plausible.Billing.QuotaTest do subscription = insert(:subscription, user_id: user.id, paddle_plan_id: enterprise_plan.paddle_plan_id) - assert Quota.monthly_pageview_limit(subscription) == 100_000 + 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") - assert Quota.monthly_pageview_limit(subscription) == :unlimited + assert Quota.Limits.monthly_pageview_limit(subscription) == :unlimited end end @@ -282,7 +282,7 @@ defmodule Plausible.Billing.QuotaTest do ] ) - assert Quota.team_member_usage(me) == 3 + assert Quota.Usage.team_member_usage(me) == 3 end test "counts the same email address as one team member" do @@ -310,7 +310,7 @@ defmodule Plausible.Billing.QuotaTest do insert(:invitation, site: site_i_own_3, inviter: me, email: "joe@plausible.test") - assert Quota.team_member_usage(me) == 2 + assert Quota.Usage.team_member_usage(me) == 2 end test "counts pending invitations as team members" do @@ -332,7 +332,7 @@ defmodule Plausible.Billing.QuotaTest do insert(:invitation, site: site_i_own, inviter: member) insert(:invitation, site: site_i_have_access, inviter: me) - assert Quota.team_member_usage(me) == 3 + assert Quota.Usage.team_member_usage(me) == 3 end test "does not count ownership transfer as a team member" do @@ -341,12 +341,12 @@ defmodule Plausible.Billing.QuotaTest do insert(:invitation, site: site_i_own, inviter: me, role: :owner) - assert Quota.team_member_usage(me) == 0 + assert Quota.Usage.team_member_usage(me) == 0 end test "returns zero when user does not have any site" do me = insert(:user) - assert Quota.team_member_usage(me) == 0 + assert Quota.Usage.team_member_usage(me) == 0 end test "does not count email report recipients as team members" do @@ -358,7 +358,7 @@ defmodule Plausible.Billing.QuotaTest do recipients: ["adam@plausible.test", "vini@plausible.test"] ) - assert Quota.team_member_usage(me) == 0 + assert Quota.Usage.team_member_usage(me) == 0 end test "excludes specific emails from limit calculation" do @@ -377,11 +377,13 @@ defmodule Plausible.Billing.QuotaTest do insert(:invitation, site: site_i_own, inviter: member) invitation = insert(:invitation, site: site_i_own, inviter: me, email: "foo@example.com") - assert Quota.team_member_usage(me) == 4 - assert Quota.team_member_usage(me, exclude_emails: "arbitrary@example.com") == 4 - assert Quota.team_member_usage(me, exclude_emails: member.email) == 3 - assert Quota.team_member_usage(me, exclude_emails: invitation.email) == 3 - assert Quota.team_member_usage(me, exclude_emails: [member.email, invitation.email]) == 2 + assert Quota.Usage.team_member_usage(me) == 4 + assert Quota.Usage.team_member_usage(me, exclude_emails: "arbitrary@example.com") == 4 + assert Quota.Usage.team_member_usage(me, exclude_emails: member.email) == 3 + assert Quota.Usage.team_member_usage(me, exclude_emails: invitation.email) == 3 + + assert Quota.Usage.team_member_usage(me, exclude_emails: [member.email, invitation.email]) == + 2 end end @@ -392,14 +394,14 @@ defmodule Plausible.Billing.QuotaTest do user_on_v2 = insert(:user, subscription: build(:subscription, paddle_plan_id: @v2_plan_id)) user_on_v3 = insert(:user, subscription: build(:subscription, paddle_plan_id: @v3_plan_id)) - assert :unlimited == Quota.team_member_limit(user_on_v1) - assert :unlimited == Quota.team_member_limit(user_on_v2) - assert :unlimited == Quota.team_member_limit(user_on_v3) + assert :unlimited == Quota.Limits.team_member_limit(user_on_v1) + assert :unlimited == Quota.Limits.team_member_limit(user_on_v2) + assert :unlimited == Quota.Limits.team_member_limit(user_on_v3) end test "returns unlimited when user is on free_10k plan" do user = insert(:user, subscription: build(:subscription, paddle_plan_id: "free_10k")) - assert :unlimited == Quota.team_member_limit(user) + assert :unlimited == Quota.Limits.team_member_limit(user) end test "returns 5 when user in on trial" do @@ -408,7 +410,7 @@ defmodule Plausible.Billing.QuotaTest do trial_expiry_date: Timex.shift(Timex.now(), days: 7) ) - assert 3 == Quota.team_member_limit(user) + assert 3 == Quota.Limits.team_member_limit(user) end test "returns the enterprise plan limit" do @@ -419,7 +421,7 @@ defmodule Plausible.Billing.QuotaTest do subscription: build(:subscription, paddle_plan_id: "123321") ) - assert 27 == Quota.team_member_limit(user) + assert 27 == Quota.Limits.team_member_limit(user) end test "reads from json file when the user is on a v4 plan" do @@ -427,22 +429,22 @@ defmodule Plausible.Billing.QuotaTest do user_on_business = insert(:user, subscription: build(:business_subscription)) - assert 3 == Quota.team_member_limit(user_on_growth) - assert 10 == Quota.team_member_limit(user_on_business) + assert 3 == Quota.Limits.team_member_limit(user_on_growth) + assert 10 == Quota.Limits.team_member_limit(user_on_business) end test "returns unlimited when user is on a v3 business plan" do user = insert(:user, subscription: build(:subscription, paddle_plan_id: @v3_business_plan_id)) - assert :unlimited == Quota.team_member_limit(user) + assert :unlimited == Quota.Limits.team_member_limit(user) end end describe "features_usage/1" do test "returns an empty list for a user/site who does not use any feature" do - assert [] == Quota.features_usage(insert(:user)) - assert [] == Quota.features_usage(insert(:site)) + assert [] == Quota.Usage.features_usage(insert(:user)) + assert [] == Quota.Usage.features_usage(insert(:site)) end test "returns [Props] when user/site uses custom props" do @@ -454,8 +456,8 @@ defmodule Plausible.Billing.QuotaTest do memberships: [build(:site_membership, user: user, role: :owner)] ) - assert [Props] == Quota.features_usage(site) - assert [Props] == Quota.features_usage(user) + assert [Props] == Quota.Usage.features_usage(site) + assert [Props] == Quota.Usage.features_usage(user) end on_ee do @@ -467,8 +469,8 @@ defmodule Plausible.Billing.QuotaTest do steps = Enum.map(goals, &%{"goal_id" => &1.id}) Plausible.Funnels.create(site, "dummy", steps) - assert [Funnels] == Quota.features_usage(site) - assert [Funnels] == Quota.features_usage(user) + assert [Funnels] == Quota.Usage.features_usage(site) + assert [Funnels] == Quota.Usage.features_usage(user) end test "returns [RevenueGoals] when user/site uses revenue goals" do @@ -476,8 +478,8 @@ defmodule Plausible.Billing.QuotaTest do site = insert(:site, memberships: [build(:site_membership, user: user, role: :owner)]) insert(:goal, currency: :USD, site: site, event_name: "Purchase") - assert [RevenueGoals] == Quota.features_usage(site) - assert [RevenueGoals] == Quota.features_usage(user) + assert [RevenueGoals] == Quota.Usage.features_usage(site) + assert [RevenueGoals] == Quota.Usage.features_usage(user) end end @@ -485,7 +487,7 @@ defmodule Plausible.Billing.QuotaTest do user = insert(:user) insert(:api_key, user: user) - assert [StatsAPI] == Quota.features_usage(user) + assert [StatsAPI] == Quota.Usage.features_usage(user) end on_ee do @@ -504,8 +506,8 @@ defmodule Plausible.Billing.QuotaTest do steps = Enum.map(goals, &%{"goal_id" => &1.id}) Plausible.Funnels.create(site, "dummy", steps) - assert [Props, Funnels, RevenueGoals] == Quota.features_usage(site) - assert [Props, Funnels, RevenueGoals] == Quota.features_usage(user) + assert [Props, Funnels, RevenueGoals] == Quota.Usage.features_usage(site) + assert [Props, Funnels, RevenueGoals] == Quota.Usage.features_usage(user) end end @@ -517,7 +519,7 @@ defmodule Plausible.Billing.QuotaTest do memberships: [build(:site_membership, user: user, role: :admin)] ) - assert [] == Quota.features_usage(user) + assert [] == Quota.Usage.features_usage(user) end end @@ -525,7 +527,7 @@ defmodule Plausible.Billing.QuotaTest do on_ee do test "users with expired trials have no access to subscription features" do user = insert(:user, trial_expiry_date: ~D[2023-01-01]) - assert [Goals] == Quota.allowed_features_for(user) + assert [Goals] == Quota.Limits.allowed_features_for(user) end end @@ -534,14 +536,14 @@ defmodule Plausible.Billing.QuotaTest do user_on_v2 = insert(:user, subscription: build(:subscription, paddle_plan_id: @v2_plan_id)) user_on_v3 = insert(:user, subscription: build(:subscription, paddle_plan_id: @v3_plan_id)) - assert [Goals, Props, StatsAPI] == Quota.allowed_features_for(user_on_v1) - assert [Goals, Props, StatsAPI] == Quota.allowed_features_for(user_on_v2) - assert [Goals, Props, StatsAPI] == Quota.allowed_features_for(user_on_v3) + assert [Goals, Props, StatsAPI] == Quota.Limits.allowed_features_for(user_on_v1) + assert [Goals, Props, StatsAPI] == Quota.Limits.allowed_features_for(user_on_v2) + assert [Goals, Props, StatsAPI] == Quota.Limits.allowed_features_for(user_on_v3) end test "returns [Goals, Props, StatsAPI] when user is on free_10k plan" do user = insert(:user, subscription: build(:subscription, paddle_plan_id: "free_10k")) - assert [Goals, Props, StatsAPI] == Quota.allowed_features_for(user) + assert [Goals, Props, StatsAPI] == Quota.Limits.allowed_features_for(user) end on_ee do @@ -560,14 +562,14 @@ defmodule Plausible.Billing.QuotaTest do insert(:subscription, user_id: user.id, paddle_plan_id: enterprise_plan.paddle_plan_id) assert [Plausible.Billing.Feature.StatsAPI, Plausible.Billing.Feature.Funnels] == - Quota.allowed_features_for(user) + Quota.Limits.allowed_features_for(user) end end test "returns all features when user in on trial" do user = insert(:user, trial_expiry_date: Timex.shift(Timex.now(), days: 7)) - assert Plausible.Billing.Feature.list() == Quota.allowed_features_for(user) + assert Plausible.Billing.Feature.list() == Quota.Limits.allowed_features_for(user) end test "returns previous plan limits for enterprise users who have not paid yet" do @@ -577,7 +579,7 @@ defmodule Plausible.Billing.QuotaTest do subscription: build(:subscription, paddle_plan_id: @v1_plan_id) ) - assert [Goals, Props, StatsAPI] == Quota.allowed_features_for(user) + assert [Goals, Props, StatsAPI] == Quota.Limits.allowed_features_for(user) end test "returns all features for enterprise users who have not upgraded yet and are on trial" do @@ -587,7 +589,7 @@ defmodule Plausible.Billing.QuotaTest do subscription: nil ) - assert Plausible.Billing.Feature.list() == Quota.allowed_features_for(user) + assert Plausible.Billing.Feature.list() == Quota.Limits.allowed_features_for(user) end test "returns old plan features for enterprise customers who are due to change a plan" do @@ -602,7 +604,7 @@ defmodule Plausible.Billing.QuotaTest do ) insert(:enterprise_plan, user_id: user.id, paddle_plan_id: "new-paddle-plan-id") - assert [Plausible.Billing.Feature.StatsAPI] == Quota.allowed_features_for(user) + assert [Plausible.Billing.Feature.StatsAPI] == Quota.Limits.allowed_features_for(user) end end @@ -619,7 +621,7 @@ defmodule Plausible.Billing.QuotaTest do pageviews: 0, date_range: date_range } - } = Quota.monthly_pageview_usage(user) + } = Quota.Usage.monthly_pageview_usage(user) assert date_range.last == Date.utc_today() assert Date.compare(date_range.first, date_range.last) == :lt @@ -650,7 +652,7 @@ defmodule Plausible.Billing.QuotaTest do pageviews: 3, date_range: %{} } - } = Quota.monthly_pageview_usage(user) + } = Quota.Usage.monthly_pageview_usage(user) end test "returns usage for user with subscription and a site" do @@ -693,7 +695,7 @@ defmodule Plausible.Billing.QuotaTest do pageviews: 0, date_range: %{} } - } = Quota.monthly_pageview_usage(user) + } = Quota.Usage.monthly_pageview_usage(user) end test "returns usage for only a subset of site IDs" do @@ -740,7 +742,7 @@ defmodule Plausible.Billing.QuotaTest do pageviews: 0, date_range: %{} } - } = Quota.monthly_pageview_usage(user, [site1.id, site3.id]) + } = Quota.Usage.monthly_pageview_usage(user, [site1.id, site3.id]) end end @@ -777,13 +779,13 @@ defmodule Plausible.Billing.QuotaTest do insert(:subscription, user_id: user.id, last_bill_date: last_bill_date) assert %{date_range: penultimate_cycle, pageviews: 2, custom_events: 3, total: 5} = - Quota.usage_cycle(user, :penultimate_cycle, nil, today) + Quota.Usage.usage_cycle(user, :penultimate_cycle, nil, today) assert %{date_range: last_cycle, pageviews: 3, custom_events: 2, total: 5} = - Quota.usage_cycle(user, :last_cycle, nil, today) + Quota.Usage.usage_cycle(user, :last_cycle, nil, today) assert %{date_range: current_cycle, pageviews: 0, custom_events: 3, total: 3} = - Quota.usage_cycle(user, :current_cycle, nil, today) + Quota.Usage.usage_cycle(user, :current_cycle, nil, today) assert penultimate_cycle == Date.range(~D[2023-04-03], ~D[2023-05-02]) assert last_cycle == Date.range(~D[2023-05-03], ~D[2023-06-02]) @@ -794,7 +796,7 @@ defmodule Plausible.Billing.QuotaTest do today = ~D[2023-06-01] assert %{date_range: last_30_days, pageviews: 4, custom_events: 1, total: 5} = - Quota.usage_cycle(user, :last_30_days, nil, today) + Quota.Usage.usage_cycle(user, :last_30_days, nil, today) assert last_30_days == Date.range(~D[2023-05-02], ~D[2023-06-01]) end @@ -817,7 +819,7 @@ defmodule Plausible.Billing.QuotaTest do insert(:subscription, user_id: user.id, last_bill_date: last_bill_date) assert %{date_range: last_cycle, pageviews: 3, custom_events: 2, total: 5} = - Quota.usage_cycle(user, :last_cycle, nil, today) + Quota.Usage.usage_cycle(user, :last_cycle, nil, today) assert last_cycle == Date.range(~D[2023-05-03], ~D[2023-06-02]) end @@ -829,13 +831,13 @@ defmodule Plausible.Billing.QuotaTest do user = insert(:user, subscription: build(:subscription, last_bill_date: last_bill_date)) assert %{date_range: penultimate_cycle} = - Quota.usage_cycle(user, :penultimate_cycle, nil, today) + Quota.Usage.usage_cycle(user, :penultimate_cycle, nil, today) assert %{date_range: last_cycle} = - Quota.usage_cycle(user, :last_cycle, nil, today) + Quota.Usage.usage_cycle(user, :last_cycle, nil, today) assert %{date_range: current_cycle} = - Quota.usage_cycle(user, :current_cycle, nil, today) + Quota.Usage.usage_cycle(user, :current_cycle, nil, today) assert penultimate_cycle == Date.range(~D[2020-12-01], ~D[2020-12-31]) assert last_cycle == Date.range(~D[2021-01-01], ~D[2021-01-31]) @@ -849,13 +851,13 @@ defmodule Plausible.Billing.QuotaTest do user = insert(:user, subscription: build(:subscription, last_bill_date: last_bill_date)) assert %{date_range: penultimate_cycle, total: 0} = - Quota.usage_cycle(user, :penultimate_cycle, nil, today) + Quota.Usage.usage_cycle(user, :penultimate_cycle, nil, today) assert %{date_range: last_cycle, total: 0} = - Quota.usage_cycle(user, :last_cycle, nil, today) + Quota.Usage.usage_cycle(user, :last_cycle, nil, today) assert %{date_range: current_cycle, total: 0} = - Quota.usage_cycle(user, :current_cycle, nil, today) + Quota.Usage.usage_cycle(user, :current_cycle, nil, today) assert penultimate_cycle == Date.range(~D[2020-11-01], ~D[2020-11-30]) assert last_cycle == Date.range(~D[2020-12-01], ~D[2020-12-31]) diff --git a/test/plausible_web/controllers/site_controller_test.exs b/test/plausible_web/controllers/site_controller_test.exs index 0ada893b7..708ce1917 100644 --- a/test/plausible_web/controllers/site_controller_test.exs +++ b/test/plausible_web/controllers/site_controller_test.exs @@ -364,7 +364,7 @@ defmodule PlausibleWeb.SiteControllerTest do }) assert redirected_to(conn) == "/example.com/snippet?site_created=true" - assert Plausible.Billing.Quota.site_usage(user) == 3 + assert Plausible.Billing.Quota.Usage.site_usage(user) == 3 end for url <- ["https://Example.com/", "HTTPS://EXAMPLE.COM/", "/Example.com/", "//Example.com/"] do diff --git a/test/plausible_web/live/choose_plan_test.exs b/test/plausible_web/live/choose_plan_test.exs index 274e14655..cb59d4026 100644 --- a/test/plausible_web/live/choose_plan_test.exs +++ b/test/plausible_web/live/choose_plan_test.exs @@ -8,6 +8,7 @@ defmodule PlausibleWeb.Live.ChoosePlanTest do alias Plausible.{Repo, Billing.Subscription} @v1_10k_yearly_plan_id "572810" + @v4_growth_10k_yearly_plan_id "857079" @v4_growth_200k_yearly_plan_id "857081" @v4_business_5m_monthly_plan_id "857111" @v3_business_10k_monthly_plan_id "857481" @@ -358,16 +359,18 @@ defmodule PlausibleWeb.Live.ChoosePlanTest do "https://plausible.io/white-label-web-analytics" end - test "displays usage", %{conn: conn, site: site} do + test "displays usage in the last cycle", %{conn: conn, site: site} do + yesterday = NaiveDateTime.utc_now() |> NaiveDateTime.add(-1, :day) + populate_stats(site, [ - build(:pageview), - build(:pageview) + build(:pageview, timestamp: yesterday), + build(:pageview, timestamp: yesterday) ]) {:ok, _lv, doc} = get_liveview(conn) assert doc =~ "You have used" assert doc =~ "2" - assert doc =~ "billable pageviews in the last 30 days" + assert doc =~ "billable pageviews in the last billing cycle" end test "gets default selected interval from current subscription plan", %{conn: conn} do @@ -375,9 +378,9 @@ defmodule PlausibleWeb.Live.ChoosePlanTest do assert class_of_element(doc, @yearly_interval_button) =~ @interval_button_active_class end - test "gets default pageview limit from current subscription plan", %{conn: conn} do + test "sets pageview slider according to last cycle usage", %{conn: conn} do {:ok, _lv, doc} = get_liveview(conn) - assert text_of_element(doc, @slider_value) == "200k" + assert text_of_element(doc, @slider_value) == "10k" end test "pageview slider changes selected volume", %{conn: conn} do @@ -401,7 +404,9 @@ defmodule PlausibleWeb.Live.ChoosePlanTest do end test "checkout button text and click-disabling CSS classes are dynamic", %{conn: conn} do - {:ok, lv, doc} = get_liveview(conn) + {:ok, lv, _doc} = get_liveview(conn) + + doc = set_slider(lv, "200k") assert text_of_element(doc, @growth_checkout_button) == "Currently on this plan" assert class_of_element(doc, @growth_checkout_button) =~ "pointer-events-none bg-gray-400" @@ -431,7 +436,7 @@ defmodule PlausibleWeb.Live.ChoosePlanTest do growth_checkout_button = find(doc, @growth_checkout_button) assert text_of_attr(growth_checkout_button, "onclick") =~ - "if (true) {window.location = '#{Routes.billing_path(conn, :change_plan_preview, @v4_growth_200k_yearly_plan_id)}'}" + "if (true) {window.location = '#{Routes.billing_path(conn, :change_plan_preview, @v4_growth_10k_yearly_plan_id)}'}" set_slider(lv, "5M") doc = element(lv, @monthly_interval_button) |> render_click() @@ -446,9 +451,9 @@ defmodule PlausibleWeb.Live.ChoosePlanTest do describe "for a user with a v4 business subscription plan" do setup [:create_user, :create_site, :log_in, :subscribe_v4_business] - test "gets default pageview limit from current subscription plan", %{conn: conn} do + test "sets pageview slider according to last cycle usage", %{conn: conn} do {:ok, _lv, doc} = get_liveview(conn) - assert text_of_element(doc, @slider_value) == "5M" + assert text_of_element(doc, @slider_value) == "10k" end test "makes it clear that the user is currently on a business tier", %{conn: conn} do @@ -462,11 +467,12 @@ defmodule PlausibleWeb.Live.ChoosePlanTest do end test "checkout button text and click-disabling CSS classes are dynamic", %{conn: conn} do - {:ok, lv, doc} = get_liveview(conn) + {:ok, lv, _doc} = get_liveview(conn) + + doc = set_slider(lv, "5M") assert text_of_element(doc, @business_checkout_button) == "Currently on this plan" assert class_of_element(doc, @business_checkout_button) =~ "pointer-events-none bg-gray-400" - assert text_of_element(doc, @growth_checkout_button) == "Downgrade to Growth" doc = element(lv, @yearly_interval_button) |> render_click() @@ -634,7 +640,10 @@ defmodule PlausibleWeb.Live.ChoosePlanTest do test "checkout buttons are disabled + notice about billing details (unless plan owned already)", %{conn: conn} do - {:ok, lv, doc} = get_liveview(conn) + {:ok, lv, _doc} = get_liveview(conn) + + doc = set_slider(lv, "200k") + assert class_of_element(doc, @growth_checkout_button) =~ "pointer-events-none bg-gray-400" assert text_of_element(doc, @growth_checkout_button) =~ "Currently on this plan" refute element_exists?(doc, "#{@growth_checkout_button} + p") @@ -664,7 +673,10 @@ defmodule PlausibleWeb.Live.ChoosePlanTest do test "checkout buttons are disabled + notice about billing details when plan not owned already", %{conn: conn} do - {:ok, lv, doc} = get_liveview(conn) + {:ok, lv, _doc} = get_liveview(conn) + + doc = set_slider(lv, "200k") + assert class_of_element(doc, @growth_checkout_button) =~ "pointer-events-none bg-gray-400" assert text_of_element(doc, @growth_checkout_button) =~ "Currently on this plan" refute element_exists?(doc, "#{@growth_checkout_button} + p") diff --git a/test/workers/check_usage_test.exs b/test/workers/check_usage_test.exs index 3cd83832b..e05be4f8d 100644 --- a/test/workers/check_usage_test.exs +++ b/test/workers/check_usage_test.exs @@ -31,8 +31,8 @@ defmodule Plausible.Workers.CheckUsageTest do test "does not send an email if account has been over the limit for one billing month", %{ user: user } do - quota_stub = - Plausible.Billing.Quota + usage_stub = + Plausible.Billing.Quota.Usage |> stub(:monthly_pageview_usage, fn _user -> %{ penultimate_cycle: %{date_range: @date_range, total: 9_000}, @@ -46,7 +46,7 @@ defmodule Plausible.Workers.CheckUsageTest do last_bill_date: Timex.shift(Timex.today(), days: -1) ) - CheckUsage.perform(nil, quota_stub) + CheckUsage.perform(nil, usage_stub) assert_no_emails_delivered() assert Repo.reload(user).grace_period == nil @@ -55,8 +55,8 @@ defmodule Plausible.Workers.CheckUsageTest do test "does not send an email if account is over the limit by less than 10%", %{ user: user } do - quota_stub = - Plausible.Billing.Quota + usage_stub = + Plausible.Billing.Quota.Usage |> stub(:monthly_pageview_usage, fn _user -> %{ penultimate_cycle: %{date_range: @date_range, total: 10_999}, @@ -70,7 +70,7 @@ defmodule Plausible.Workers.CheckUsageTest do last_bill_date: Timex.shift(Timex.today(), days: -1) ) - CheckUsage.perform(nil, quota_stub) + CheckUsage.perform(nil, usage_stub) assert_no_emails_delivered() assert Repo.reload(user).grace_period == nil @@ -79,8 +79,8 @@ defmodule Plausible.Workers.CheckUsageTest do test "sends an email when an account is over their limit for two consecutive billing months", %{ user: user } do - quota_stub = - Plausible.Billing.Quota + usage_stub = + Plausible.Billing.Quota.Usage |> stub(:monthly_pageview_usage, fn _user -> %{ penultimate_cycle: %{date_range: @date_range, total: 11_000}, @@ -94,7 +94,7 @@ defmodule Plausible.Workers.CheckUsageTest do last_bill_date: Timex.shift(Timex.today(), days: -1) ) - CheckUsage.perform(nil, quota_stub) + CheckUsage.perform(nil, usage_stub) assert_email_delivered_with( to: [user], @@ -107,8 +107,8 @@ defmodule Plausible.Workers.CheckUsageTest do test "sends an email suggesting enterprise plan when usage is greater than 10M ", %{ user: user } do - quota_stub = - Plausible.Billing.Quota + usage_stub = + Plausible.Billing.Quota.Usage |> stub(:monthly_pageview_usage, fn _user -> %{ penultimate_cycle: %{date_range: @date_range, total: 11_000_000}, @@ -122,7 +122,7 @@ defmodule Plausible.Workers.CheckUsageTest do last_bill_date: Timex.shift(Timex.today(), days: -1) ) - CheckUsage.perform(nil, quota_stub) + CheckUsage.perform(nil, usage_stub) assert_delivered_email_matches(%{html_body: html_body}) @@ -136,8 +136,8 @@ defmodule Plausible.Workers.CheckUsageTest do |> Plausible.Auth.GracePeriod.start_changeset() |> Repo.update!() - quota_stub = - Plausible.Billing.Quota + usage_stub = + Plausible.Billing.Quota.Usage |> stub(:monthly_pageview_usage, fn _user -> %{ penultimate_cycle: %{date_range: @date_range, total: 11_000}, @@ -151,7 +151,7 @@ defmodule Plausible.Workers.CheckUsageTest do last_bill_date: Timex.shift(Timex.today(), days: -1) ) - CheckUsage.perform(nil, quota_stub) + CheckUsage.perform(nil, usage_stub) assert_no_emails_delivered() assert Repo.reload(user).grace_period.id == existing_grace_period.id @@ -160,8 +160,8 @@ defmodule Plausible.Workers.CheckUsageTest do test "recommends a plan to upgrade to", %{ user: user } do - quota_stub = - Plausible.Billing.Quota + usage_stub = + Plausible.Billing.Quota.Usage |> stub(:monthly_pageview_usage, fn _user -> %{ penultimate_cycle: %{date_range: @date_range, total: 11_000}, @@ -175,7 +175,7 @@ defmodule Plausible.Workers.CheckUsageTest do last_bill_date: Timex.shift(Timex.today(), days: -1) ) - CheckUsage.perform(nil, quota_stub) + CheckUsage.perform(nil, usage_stub) assert_delivered_email_matches(%{ html_body: html_body @@ -185,8 +185,8 @@ defmodule Plausible.Workers.CheckUsageTest do end test "clears grace period when plan is applicable again", %{user: user} do - quota_stub = - Plausible.Billing.Quota + usage_stub = + Plausible.Billing.Quota.Usage |> stub(:monthly_pageview_usage, fn _user -> %{ penultimate_cycle: %{date_range: @date_range, total: 11_000}, @@ -200,11 +200,11 @@ defmodule Plausible.Workers.CheckUsageTest do last_bill_date: Timex.shift(Timex.today(), days: -1) ) - CheckUsage.perform(nil, quota_stub) + CheckUsage.perform(nil, usage_stub) assert user |> Repo.reload() |> Plausible.Auth.GracePeriod.active?() - quota_stub = - Plausible.Billing.Quota + usage_stub = + Plausible.Billing.Quota.Usage |> stub(:monthly_pageview_usage, fn _user -> %{ penultimate_cycle: %{date_range: @date_range, total: 11_000}, @@ -212,7 +212,7 @@ defmodule Plausible.Workers.CheckUsageTest do } end) - CheckUsage.perform(nil, quota_stub) + CheckUsage.perform(nil, usage_stub) refute user |> Repo.reload() |> Plausible.Auth.GracePeriod.active?() end @@ -223,8 +223,8 @@ defmodule Plausible.Workers.CheckUsageTest do |> Plausible.Auth.GracePeriod.start_manual_lock_changeset() |> Repo.update!() - quota_stub = - Plausible.Billing.Quota + usage_stub = + Plausible.Billing.Quota.Usage |> stub(:monthly_pageview_usage, fn _user -> %{ penultimate_cycle: %{date_range: @date_range, total: 1_100_000}, @@ -240,7 +240,7 @@ defmodule Plausible.Workers.CheckUsageTest do last_bill_date: Timex.shift(Timex.today(), days: -1) ) - CheckUsage.perform(nil, quota_stub) + CheckUsage.perform(nil, usage_stub) assert_no_emails_delivered() assert Repo.reload(user).grace_period.id == existing_grace_period.id @@ -250,8 +250,8 @@ defmodule Plausible.Workers.CheckUsageTest do %{ user: user } do - quota_stub = - Plausible.Billing.Quota + usage_stub = + Plausible.Billing.Quota.Usage |> stub(:monthly_pageview_usage, fn _user -> %{ penultimate_cycle: %{date_range: @date_range, total: 1_100_000}, @@ -267,7 +267,7 @@ defmodule Plausible.Workers.CheckUsageTest do last_bill_date: Timex.shift(Timex.today(), days: -1) ) - CheckUsage.perform(nil, quota_stub) + CheckUsage.perform(nil, usage_stub) assert_email_delivered_with( to: [{nil, "enterprise@plausible.io"}], @@ -279,8 +279,8 @@ defmodule Plausible.Workers.CheckUsageTest do %{ user: user } do - quota_stub = - Plausible.Billing.Quota + usage_stub = + Plausible.Billing.Quota.Usage |> stub(:monthly_pageview_usage, fn _user -> %{ penultimate_cycle: %{date_range: @date_range, total: 1}, @@ -300,7 +300,7 @@ defmodule Plausible.Workers.CheckUsageTest do last_bill_date: Timex.shift(Timex.today(), days: -1) ) - CheckUsage.perform(nil, quota_stub) + CheckUsage.perform(nil, usage_stub) assert_email_delivered_with( to: [{nil, "enterprise@plausible.io"}], @@ -309,8 +309,8 @@ defmodule Plausible.Workers.CheckUsageTest do end test "starts grace period when plan is outgrown", %{user: user} do - quota_stub = - Plausible.Billing.Quota + usage_stub = + Plausible.Billing.Quota.Usage |> stub(:monthly_pageview_usage, fn _user -> %{ penultimate_cycle: %{date_range: @date_range, total: 1_100_000}, @@ -326,7 +326,7 @@ defmodule Plausible.Workers.CheckUsageTest do last_bill_date: Timex.shift(Timex.today(), days: -1) ) - CheckUsage.perform(nil, quota_stub) + CheckUsage.perform(nil, usage_stub) assert user |> Repo.reload() |> Plausible.Auth.GracePeriod.active?() end end @@ -335,8 +335,8 @@ defmodule Plausible.Workers.CheckUsageTest do test "checks usage one day after the last_bill_date", %{ user: user } do - quota_stub = - Plausible.Billing.Quota + usage_stub = + Plausible.Billing.Quota.Usage |> stub(:monthly_pageview_usage, fn _user -> %{ penultimate_cycle: %{date_range: @date_range, total: 11_000}, @@ -350,7 +350,7 @@ defmodule Plausible.Workers.CheckUsageTest do last_bill_date: Timex.shift(Timex.today(), days: -1) ) - CheckUsage.perform(nil, quota_stub) + CheckUsage.perform(nil, usage_stub) assert_email_delivered_with( to: [user], @@ -361,8 +361,8 @@ defmodule Plausible.Workers.CheckUsageTest do test "does not check exactly one month after last_bill_date", %{ user: user } do - quota_stub = - Plausible.Billing.Quota + usage_stub = + Plausible.Billing.Quota.Usage |> stub(:monthly_pageview_usage, fn _user -> %{ penultimate_cycle: %{date_range: @date_range, total: 11_000}, @@ -376,7 +376,7 @@ defmodule Plausible.Workers.CheckUsageTest do last_bill_date: ~D[2021-03-28] ) - CheckUsage.perform(nil, quota_stub, ~D[2021-03-28]) + CheckUsage.perform(nil, usage_stub, ~D[2021-03-28]) assert_no_emails_delivered() end @@ -385,8 +385,8 @@ defmodule Plausible.Workers.CheckUsageTest do %{ user: user } do - quota_stub = - Plausible.Billing.Quota + usage_stub = + Plausible.Billing.Quota.Usage |> stub(:monthly_pageview_usage, fn _user -> %{ penultimate_cycle: %{date_range: @date_range, total: 11_000}, @@ -400,7 +400,7 @@ defmodule Plausible.Workers.CheckUsageTest do last_bill_date: ~D[2021-06-29] ) - CheckUsage.perform(nil, quota_stub, ~D[2021-08-30]) + CheckUsage.perform(nil, usage_stub, ~D[2021-08-30]) assert_email_delivered_with( to: [user],