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],