Refactor Plausible.Billing.Plans module (#3268)

This pull request introduces a series of improvements to Plausible.Billing.Plans, including:

* Tag the JSON file with the plan version
* Rename the JSON field limit to monthly_pageview_limit
* Move site_limit function to Billing.Plans
* Refactor subscription_interval, allowance and site_limit functions
* Remove unused AnalyzePlans task
This commit is contained in:
Vini Brasil 2023-08-16 13:38:38 -03:00 committed by GitHub
parent 3f9ca35d58
commit 34f1ddfc8c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 305 additions and 281 deletions

View File

@ -8,7 +8,6 @@ ENVIRONMENT=test
MAILER_ADAPTER=Bamboo.TestAdapter
ENABLE_EMAIL_VERIFICATION=true
SELFHOST=false
SITE_LIMIT=3
HCAPTCHA_SITEKEY=test
HCAPTCHA_SECRET=scottiger
IP_GEOLOCATION_DB=test/priv/GeoLite2-City-Test.mmdb

View File

@ -204,11 +204,6 @@ custom_script_name =
config_dir
|> get_var_from_path_or_env("CUSTOM_SCRIPT_NAME", "script")
{site_limit, ""} =
config_dir
|> get_var_from_path_or_env("SITE_LIMIT", "50")
|> Integer.parse()
site_limit_exempt =
config_dir
|> get_var_from_path_or_env("SITE_LIMIT_EXEMPT", "")
@ -241,7 +236,6 @@ config :plausible,
environment: env,
mailer_email: mailer_email,
super_admin_user_ids: super_admin_user_ids,
site_limit: site_limit,
site_limit_exempt: site_limit_exempt,
is_selfhost: is_selfhost,
custom_script_name: custom_script_name,

View File

@ -1,60 +0,0 @@
defmodule Mix.Tasks.AnalyzePlans do
use Mix.Task
use Plausible.Repo
# coveralls-ignore-start
def run(_) do
Mix.Task.run("app.start")
res =
Repo.all(
from s in Plausible.Billing.Subscription,
where: s.status == "active",
group_by: s.paddle_plan_id,
select: {s.paddle_plan_id, count(s)}
)
res =
Enum.map(res, fn {plan_id, count} ->
plan = Plausible.Billing.Plans.find(plan_id)
if plan do
is_monthly = plan_id == plan.monthly_product_id
monthly_revenue =
if is_monthly do
price(plan.monthly_cost)
else
price(plan.yearly_cost) / 12
end
{PlausibleWeb.StatsView.large_number_format(plan.limit), monthly_revenue, count}
end
end)
|> Enum.filter(& &1)
res =
Enum.reduce(res, %{}, fn {limit, revenue, count}, acc ->
total_revenue = revenue * count
Map.update(acc, limit, {total_revenue, count}, fn {ex_rev, ex_count} ->
{ex_rev + total_revenue, ex_count + count}
end)
end)
total_revenue = round(Enum.reduce(res, 0, fn {_, {revenue, _}}, sum -> sum + revenue end))
for {limit, {rev, _}} <- res do
percentage = round(rev / total_revenue * 100)
IO.puts(
"The #{limit} plan makes up #{percentage}% of total revenue ($#{round(rev)} / $#{total_revenue})"
)
end
end
defp price("$" <> nr) do
String.to_integer(nr)
end
end

View File

@ -156,37 +156,6 @@ defmodule Plausible.Billing do
Plausible.Stats.Clickhouse.usage_breakdown(site_ids)
end
@doc """
Returns the number of sites that an account is allowed to have. Accounts for
grandfathering old accounts to unlimited websites and ignores site limit on self-hosted
installations.
"""
@limit_accounts_since ~D[2021-05-05]
def sites_limit(user) do
user = Plausible.Repo.preload(user, :enterprise_plan)
cond do
Timex.before?(user.inserted_at, @limit_accounts_since) ->
nil
Application.get_env(:plausible, :is_selfhost) ->
nil
user.email in Application.get_env(:plausible, :site_limit_exempt) ->
nil
user.enterprise_plan ->
if has_active_enterprise_subscription(user) do
nil
else
Application.get_env(:plausible, :site_limit)
end
true ->
Application.get_env(:plausible, :site_limit)
end
end
defp handle_subscription_created(params) do
params =
if present?(params["passthrough"]) do
@ -255,18 +224,6 @@ defmodule Plausible.Billing do
end
end
defp has_active_enterprise_subscription(user) do
Plausible.Repo.exists?(
from(s in Plausible.Billing.Subscription,
join: e in Plausible.Billing.EnterprisePlan,
on: s.user_id == e.user_id and s.paddle_plan_id == e.paddle_plan_id,
where: s.user_id == ^user.id,
where: s.paddle_plan_id == ^user.enterprise_plan.paddle_plan_id,
where: s.status == "active"
)
)
end
defp format_subscription(params) do
%{
paddle_subscription_id: params["subscription_id"],

View File

@ -2,12 +2,14 @@ defmodule Plausible.Billing.Plan do
@moduledoc false
@derive Jason.Encoder
@enforce_keys ~w(limit volume monthly_cost yearly_cost monthly_product_id yearly_product_id)a
@enforce_keys ~w(kind site_limit monthly_pageview_limit volume monthly_cost yearly_cost monthly_product_id yearly_product_id)a
defstruct @enforce_keys
@type t() ::
%__MODULE__{
limit: non_neg_integer(),
kind: atom(),
monthly_pageview_limit: non_neg_integer(),
site_limit: non_neg_integer(),
volume: String.t(),
monthly_cost: String.t() | nil,
yearly_cost: String.t() | nil,
@ -34,7 +36,14 @@ defmodule Plausible.Billing.Plans do
path
|> File.read!()
|> Jason.decode!(keys: :atoms!)
|> Enum.map(&Map.put(&1, :volume, PlausibleWeb.StatsView.large_number_format(&1.limit)))
|> Enum.map(
&Map.put(
&1,
:volume,
PlausibleWeb.StatsView.large_number_format(&1.monthly_pageview_limit)
)
)
|> Enum.map(&Map.put(&1, :kind, String.to_atom(&1.kind)))
|> Enum.map(&struct!(Plausible.Billing.Plan, &1))
Module.put_attribute(__MODULE__, f, contents)
@ -48,7 +57,7 @@ defmodule Plausible.Billing.Plans do
As new versions of plans are introduced, users who were on old plans can
still choose from old plans.
"""
def for_user(user) do
def for_user(%Plausible.Auth.User{} = user) do
user = Plausible.Users.with_subscription(user)
cond do
@ -96,47 +105,82 @@ defmodule Plausible.Billing.Plans do
end)
end
def subscription_interval(%Plausible.Billing.Subscription{paddle_plan_id: "free_10k"}),
do: "N/A"
@limit_sites_since ~D[2021-05-05]
@spec site_limit(Plausible.Auth.User.t()) :: non_neg_integer() | :unlimited
@doc """
Returns the limit of sites a user can have.
For enterprise customers, returns :unlimited. The site limit is checked in a
background job so as to avoid service disruption.
"""
def site_limit(user) do
cond do
Application.get_env(:plausible, :is_selfhost) -> :unlimited
user.email in Application.get_env(:plausible, :site_limit_exempt) -> :unlimited
Timex.before?(user.inserted_at, @limit_sites_since) -> :unlimited
true -> get_site_limit_from_plan(user)
end
end
@site_limit_for_trials 50
@site_limit_for_free_10k 50
defp get_site_limit_from_plan(user) do
user = Plausible.Users.with_subscription(user)
case get_subscription_plan(user.subscription) do
%Plausible.Billing.EnterprisePlan{} -> :unlimited
%Plausible.Billing.Plan{site_limit: site_limit} -> site_limit
:free_10k -> @site_limit_for_free_10k
nil -> @site_limit_for_trials
end
end
defp get_subscription_plan(subscription) do
if subscription && subscription.paddle_plan_id == "free_10k" do
:free_10k
else
find(subscription) || get_enterprise_plan(subscription)
end
end
def subscription_interval(subscription) do
case find(subscription.paddle_plan_id) do
nil ->
enterprise_plan = get_enterprise_plan(subscription)
case get_subscription_plan(subscription) do
%Plausible.Billing.EnterprisePlan{billing_interval: interval} ->
interval
enterprise_plan && enterprise_plan.billing_interval
plan ->
if subscription.paddle_plan_id == plan.monthly_product_id do
%Plausible.Billing.Plan{} = plan ->
if plan.monthly_product_id == subscription.paddle_plan_id do
"monthly"
else
"yearly"
end
_any ->
"N/A"
end
end
def allowance(%Plausible.Billing.Subscription{paddle_plan_id: "free_10k"}), do: 10_000
@spec allowance(Plausible.Billing.Subscription.t()) :: non_neg_integer() | nil
def allowance(subscription) do
found = find(subscription.paddle_plan_id)
case get_subscription_plan(subscription) do
%Plausible.Billing.EnterprisePlan{monthly_pageview_limit: limit} ->
limit
if found do
Map.fetch!(found, :limit)
else
enterprise_plan = get_enterprise_plan(subscription)
%Plausible.Billing.Plan{monthly_pageview_limit: limit} ->
limit
if enterprise_plan do
enterprise_plan.monthly_pageview_limit
else
:free_10k ->
10_000
_any ->
Sentry.capture_message("Unknown allowance for plan",
extra: %{
paddle_plan_id: subscription.paddle_plan_id
}
extra: %{paddle_plan_id: subscription.paddle_plan_id}
)
end
end
end
defp get_enterprise_plan(nil), do: nil
defp get_enterprise_plan(%Plausible.Billing.Subscription{} = subscription) do
Repo.get_by(Plausible.Billing.EnterprisePlan,
user_id: subscription.user_id,
@ -147,21 +191,21 @@ defmodule Plausible.Billing.Plans do
@enterprise_level_usage 10_000_000
@spec suggest(Plausible.Auth.User.t(), non_neg_integer()) :: Plausible.Billing.Plan.t()
@doc """
Returns the most appropriate plan for a user based on their usage during a
Returns the most appropriate plan for a user based on their usage during a
given cycle.
If the usage during the cycle exceeds the enterprise-level threshold, or if
the user already belongs to an enterprise plan, it suggests the :enterprise
If the usage during the cycle exceeds the enterprise-level threshold, or if
the user already belongs to an enterprise plan, it suggests the :enterprise
plan.
Otherwise, it recommends the plan where the cycle usage falls just under the
Otherwise, it recommends the plan where the cycle usage falls just under the
plan's limit from the available options for the user.
"""
def suggest(user, usage_during_cycle) do
cond do
usage_during_cycle > @enterprise_level_usage -> :enterprise
Plausible.Auth.enterprise?(user) -> :enterprise
true -> Enum.find(for_user(user), &(usage_during_cycle < &1.limit))
true -> Enum.find(for_user(user), &(usage_during_cycle < &1.monthly_pageview_limit))
end
end

View File

@ -13,13 +13,10 @@ defmodule Plausible.Sites do
Ecto.Multi.new()
|> Ecto.Multi.run(:limit, fn _, _ ->
limit = Plausible.Billing.sites_limit(user)
count = owned_sites_count(user)
if count >= limit do
{:error, limit}
else
{:ok, count}
case {Plausible.Billing.Plans.site_limit(user), owned_sites_count(user)} do
{:unlimited, actual} -> {:ok, actual}
{limit, actual} when actual >= limit -> {:error, limit}
{_limit, actual} -> {:ok, actual}
end
end)
|> Ecto.Multi.insert(:site, site_changeset)

View File

@ -51,17 +51,17 @@ defmodule PlausibleWeb.SiteController do
def new(conn, _params) do
current_user = conn.assigns[:current_user]
owned_site_count = Plausible.Sites.owned_sites_count(current_user)
site_limit = Plausible.Billing.sites_limit(current_user)
is_at_limit = site_limit && owned_site_count >= site_limit
is_first_site = owned_site_count == 0
changeset = Plausible.Site.changeset(%Plausible.Site{})
{site_limit, is_at_limit} =
case Plausible.Billing.Plans.site_limit(current_user) do
:unlimited -> {:unlimited, false}
limit when is_integer(limit) -> {limit, owned_site_count >= limit}
end
render(conn, "new.html",
changeset: changeset,
is_first_site: is_first_site,
changeset: Plausible.Site.changeset(%Plausible.Site{}),
is_first_site: owned_site_count == 0,
is_at_limit: is_at_limit,
site_limit: site_limit,
layout: {PlausibleWeb.LayoutView, "focus.html"}

View File

@ -1,72 +1,92 @@
[
{
"limit":10000,
"kind":"growth",
"monthly_pageview_limit":10000,
"monthly_cost":"$6",
"monthly_product_id":"558018",
"yearly_cost":"$48",
"yearly_product_id":"572810"
"yearly_product_id":"572810",
"site_limit":50
},
{
"limit":100000,
"kind":"growth",
"monthly_pageview_limit":100000,
"monthly_cost":"$12",
"monthly_product_id":"558745",
"yearly_cost":"$96",
"yearly_product_id":"590752"
"yearly_product_id":"590752",
"site_limit":50
},
{
"limit":200000,
"kind":"growth",
"monthly_pageview_limit":200000,
"monthly_cost":"$18",
"monthly_product_id":"597485",
"yearly_cost":"$144",
"yearly_product_id":"597486"
"yearly_product_id":"597486",
"site_limit":50
},
{
"limit":500000,
"kind":"growth",
"monthly_pageview_limit":500000,
"monthly_cost":"$27",
"monthly_product_id":"597487",
"yearly_cost":"$216",
"yearly_product_id":"597488"
"yearly_product_id":"597488",
"site_limit":50
},
{
"limit":1000000,
"kind":"growth",
"monthly_pageview_limit":1000000,
"monthly_cost":"$48",
"monthly_product_id":"597642",
"yearly_cost":"$384",
"yearly_product_id":"597643"
"yearly_product_id":"597643",
"site_limit":50
},
{
"limit":2000000,
"kind":"growth",
"monthly_pageview_limit":2000000,
"monthly_cost":"$69",
"monthly_product_id":"597309",
"yearly_cost":"$552",
"yearly_product_id":"597310"
"yearly_product_id":"597310",
"site_limit":50
},
{
"limit":5000000,
"kind":"growth",
"monthly_pageview_limit":5000000,
"monthly_cost":"$99",
"monthly_product_id":"597311",
"yearly_cost":"$792",
"yearly_product_id":"597312"
"yearly_product_id":"597312",
"site_limit":50
},
{
"limit":10000000,
"kind":"growth",
"monthly_pageview_limit":10000000,
"monthly_cost":"$150",
"monthly_product_id":"642352",
"yearly_cost":"$1200",
"yearly_product_id":"642354"
"yearly_product_id":"642354",
"site_limit":50
},
{
"limit":20000000,
"kind":"growth",
"monthly_pageview_limit":20000000,
"monthly_cost":"$225",
"monthly_product_id":"642355",
"yearly_cost":"$1800",
"yearly_product_id":"642356"
"yearly_product_id":"642356",
"site_limit":50
},
{
"limit":50000000,
"kind":"growth",
"monthly_pageview_limit":50000000,
"monthly_cost":"$330",
"monthly_product_id":"650652",
"yearly_cost":"$2640",
"yearly_product_id":"650653"
"yearly_product_id":"650653",
"site_limit":50
}
]

View File

@ -1,72 +1,92 @@
[
{
"limit":10000,
"kind":"growth",
"monthly_pageview_limit":10000,
"monthly_cost":"$6",
"monthly_product_id":"654177",
"yearly_cost":"$60",
"yearly_product_id":"653232"
"yearly_product_id":"653232",
"site_limit":50
},
{
"limit":100000,
"kind":"growth",
"monthly_pageview_limit":100000,
"monthly_cost":"$12",
"monthly_product_id":"654178",
"yearly_cost":"$120",
"yearly_product_id":"653234"
"yearly_product_id":"653234",
"site_limit":50
},
{
"limit":200000,
"kind":"growth",
"monthly_pageview_limit":200000,
"monthly_cost":"$20",
"monthly_product_id":"653237",
"yearly_cost":"$200",
"yearly_product_id":"653236"
"yearly_product_id":"653236",
"site_limit":50
},
{
"limit":500000,
"kind":"growth",
"monthly_pageview_limit":500000,
"monthly_cost":"$30",
"monthly_product_id":"653238",
"yearly_cost":"$300",
"yearly_product_id":"653239"
"yearly_product_id":"653239",
"site_limit":50
},
{
"limit":1000000,
"kind":"growth",
"monthly_pageview_limit":1000000,
"monthly_cost":"$50",
"monthly_product_id":"653240",
"yearly_cost":"$500",
"yearly_product_id":"653242"
"yearly_product_id":"653242",
"site_limit":50
},
{
"limit":2000000,
"kind":"growth",
"monthly_pageview_limit":2000000,
"monthly_cost":"$70",
"monthly_product_id":"653253",
"yearly_cost":"$700",
"yearly_product_id":"653254"
"yearly_product_id":"653254",
"site_limit":50
},
{
"limit":5000000,
"kind":"growth",
"monthly_pageview_limit":5000000,
"monthly_cost":"$100",
"monthly_product_id":"653255",
"yearly_cost":"$1000",
"yearly_product_id":"653256"
"yearly_product_id":"653256",
"site_limit":50
},
{
"limit":10000000,
"kind":"growth",
"monthly_pageview_limit":10000000,
"monthly_cost":"$150",
"monthly_product_id":"654181",
"yearly_cost":"$1500",
"yearly_product_id":"653257"
"yearly_product_id":"653257",
"site_limit":50
},
{
"limit":20000000,
"kind":"growth",
"monthly_pageview_limit":20000000,
"monthly_cost":"$225",
"monthly_product_id":"654182",
"yearly_cost":"$2250",
"yearly_product_id":"653258"
"yearly_product_id":"653258",
"site_limit":50
},
{
"limit":50000000,
"kind":"growth",
"monthly_pageview_limit":50000000,
"monthly_cost":"$330",
"monthly_product_id":"654183",
"yearly_cost":"$3300",
"yearly_product_id":"653259"
"yearly_product_id":"653259",
"site_limit":50
}
]

View File

@ -1,59 +1,74 @@
[
{
"limit":10000,
"kind":"growth",
"monthly_pageview_limit":10000,
"monthly_cost":"$9",
"monthly_product_id":"749342",
"yearly_cost":"$90",
"yearly_product_id":"749343"
"yearly_product_id":"749343",
"site_limit":50
},
{
"limit":100000,
"kind":"growth",
"monthly_pageview_limit":100000,
"monthly_cost":"$19",
"monthly_product_id":"749344",
"yearly_cost":"$190",
"yearly_product_id":"749345"
"yearly_product_id":"749345",
"site_limit":50
},
{
"limit":200000,
"kind":"growth",
"monthly_pageview_limit":200000,
"monthly_cost":"$29",
"monthly_product_id":"749346",
"yearly_cost":"$290",
"yearly_product_id":"749347"
"yearly_product_id":"749347",
"site_limit":50
},
{
"limit":500000,
"kind":"growth",
"monthly_pageview_limit":500000,
"monthly_cost":"$49",
"monthly_product_id":"749348",
"yearly_cost":"$490",
"yearly_product_id":"749349"
"yearly_product_id":"749349",
"site_limit":50
},
{
"limit":1000000,
"kind":"growth",
"monthly_pageview_limit":1000000,
"monthly_cost":"$69",
"monthly_product_id":"749350",
"yearly_cost":"$690",
"yearly_product_id":"749352"
"yearly_product_id":"749352",
"site_limit":50
},
{
"limit":2000000,
"kind":"growth",
"monthly_pageview_limit":2000000,
"monthly_cost":"$89",
"monthly_product_id":"749353",
"yearly_cost":"$890",
"yearly_product_id":"749355"
"yearly_product_id":"749355",
"site_limit":50
},
{
"limit":5000000,
"kind":"growth",
"monthly_pageview_limit":5000000,
"monthly_cost":"$129",
"monthly_product_id":"749356",
"yearly_cost":"$1290",
"yearly_product_id":"749357"
"yearly_product_id":"749357",
"site_limit":50
},
{
"limit":10000000,
"kind":"growth",
"monthly_pageview_limit":10000000,
"monthly_cost":"$169",
"monthly_product_id":"749358",
"yearly_cost":"$1690",
"yearly_product_id":"749359"
"yearly_product_id":"749359",
"site_limit":50
}
]

View File

@ -1,16 +1,20 @@
[
{
"limit":10000,
"kind":"growth",
"monthly_pageview_limit":10000,
"monthly_product_id":"19878",
"yearly_product_id":"20127",
"monthly_cost":"$6",
"yearly_cost":"$60"
"yearly_cost":"$60",
"site_limit":50
},
{
"limit":100000,
"kind":"growth",
"monthly_pageview_limit":100000,
"monthly_product_id":"20657",
"yearly_product_id":"20658",
"monthly_cost":"$12.34",
"yearly_cost":"$120.34"
"yearly_cost":"$120.34",
"site_limit":50
}
]

View File

@ -1,9 +1,11 @@
[
{
"limit":150000000,
"kind":"growth",
"monthly_pageview_limit":150000000,
"yearly_product_id":"648089",
"yearly_cost":"$4800",
"monthly_product_id":null,
"monthly_cost":null
"monthly_cost":null,
"site_limit":50
}
]

View File

@ -1,9 +1,11 @@
[
{
"limit":10000000,
"kind":"growth",
"monthly_pageview_limit":10000000,
"monthly_product_id":"655350",
"monthly_cost":"$250",
"yearly_product_id":null,
"yearly_cost":null
"yearly_cost":null,
"site_limit":50
}
]

View File

@ -49,52 +49,6 @@ defmodule Plausible.BillingTest do
end
end
describe "sites_limit" do
test "is the globally configured site limit for regular accounts" do
user = insert(:user, subscription: build(:subscription))
assert Billing.sites_limit(user) == Application.get_env(:plausible, :site_limit)
end
test "is limited for enterprise customers who have not upgraded yet" do
enterprise_plan_paddle_id = "123321"
user =
insert(:user,
enterprise_plan: build(:enterprise_plan, paddle_plan_id: enterprise_plan_paddle_id),
subscription: build(:subscription, paddle_plan_id: "99999")
)
assert Billing.sites_limit(user) == Application.get_env(:plausible, :site_limit)
end
test "is unlimited for enterprise customers. Their site limit is checked in a background job so as to avoid service disruption" do
enterprise_plan_paddle_id = "123321"
user =
insert(:user,
enterprise_plan: build(:enterprise_plan, paddle_plan_id: enterprise_plan_paddle_id),
subscription: build(:subscription, paddle_plan_id: enterprise_plan_paddle_id)
)
assert Billing.sites_limit(user) == nil
end
test "is unlimited for enterprise customers who are due to change a plan" do
enterprise_plan_paddle_id = "123321"
user =
insert(:user,
enterprise_plan: build(:enterprise_plan, paddle_plan_id: enterprise_plan_paddle_id),
subscription: build(:subscription, paddle_plan_id: enterprise_plan_paddle_id)
)
insert(:enterprise_plan, user_id: user.id, paddle_plan_id: "new-paddle-plan-id")
assert Billing.sites_limit(user) == nil
end
end
describe "last_two_billing_cycles" do
test "billing on the 1st" do
last_bill_date = ~D[2021-01-01]

View File

@ -88,22 +88,22 @@ defmodule Plausible.Billing.PlansTest do
user = insert(:user, subscription: build(:subscription, paddle_plan_id: @v1_plan_id))
assert %Plausible.Billing.Plan{
limit: 100_000,
monthly_pageview_limit: 100_000,
monthly_cost: "$12",
monthly_product_id: "558745",
volume: "100k",
yearly_cost: "$96",
yearly_product_id: "590752"
} == Plans.suggest(user, 10_000)
} = Plans.suggest(user, 10_000)
assert %Plausible.Billing.Plan{
limit: 200_000,
monthly_pageview_limit: 200_000,
monthly_cost: "$18",
monthly_product_id: "597485",
volume: "200k",
yearly_cost: "$144",
yearly_product_id: "597486"
} == Plans.suggest(user, 100_000)
} = Plans.suggest(user, 100_000)
end
test "returns nil when user has enterprise-level usage" do
@ -153,4 +153,86 @@ defmodule Plausible.Billing.PlansTest do
] == Plans.yearly_product_ids()
end
end
describe "site_limit/1" do
test "returns 50 when user is on an old plan" do
user_on_v1 = insert(:user, subscription: build(:subscription, paddle_plan_id: @v1_plan_id))
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 == Plans.site_limit(user_on_v1)
assert 50 == Plans.site_limit(user_on_v2)
assert 50 == Plans.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 == Plans.site_limit(user)
end
test "returns unlimited when user is on an enterprise plan" do
user = insert(:user)
enterprise_plan =
insert(:enterprise_plan,
user_id: user.id,
monthly_pageview_limit: 100_000,
site_limit: 500
)
_subscription =
insert(:subscription, user_id: user.id, paddle_plan_id: enterprise_plan.paddle_plan_id)
assert :unlimited == Plans.site_limit(user)
end
test "returns 50 when user in on trial" do
user = insert(:user, trial_expiry_date: Timex.shift(Timex.now(), days: 7))
assert 50 == Plans.site_limit(user)
user = insert(:user, trial_expiry_date: Timex.shift(Timex.now(), days: -7))
assert 50 == Plans.site_limit(user)
end
test "returns the subscription limit for enterprise users who have not paid yet" do
user =
insert(:user,
enterprise_plan: build(:enterprise_plan, paddle_plan_id: "123321"),
subscription: build(:subscription, paddle_plan_id: @v1_plan_id)
)
assert 50 == Plans.site_limit(user)
end
test "returns 50 for enterprise users who have not upgraded yet and are on trial" do
user =
insert(:user,
enterprise_plan: build(:enterprise_plan, paddle_plan_id: "123321"),
subscription: nil
)
assert 50 == Plans.site_limit(user)
end
test "is unlimited for enterprise customers" do
user =
insert(:user,
enterprise_plan: build(:enterprise_plan, paddle_plan_id: "123321"),
subscription: build(:subscription, paddle_plan_id: "123321")
)
assert :unlimited == Plans.site_limit(user)
end
test "is unlimited for enterprise customers who are due to change a plan" do
user =
insert(:user,
enterprise_plan: build(:enterprise_plan, paddle_plan_id: "old-paddle-plan-id"),
subscription: build(:subscription, paddle_plan_id: "old-paddle-plan-id")
)
insert(:enterprise_plan, user_id: user.id, paddle_plan_id: "new-paddle-plan-id")
assert :unlimited == Plans.site_limit(user)
end
end
end

View File

@ -62,10 +62,7 @@ defmodule PlausibleWeb.Api.ExternalSitesControllerTest do
end
test "does not allow creating more sites than the limit", %{conn: conn, user: user} do
patch_env(:site_limit, 3)
insert(:site, members: [user])
insert(:site, members: [user])
insert(:site, members: [user])
insert_list(50, :site, members: [user])
conn =
post(conn, "/api/v1/sites", %{
@ -75,7 +72,7 @@ defmodule PlausibleWeb.Api.ExternalSitesControllerTest do
assert json_response(conn, 403) == %{
"error" =>
"Your account has reached the limit of 3 sites per account. Please contact hello@plausible.io to unlock more sites."
"Your account has reached the limit of 50 sites per account. Please contact hello@plausible.io to unlock more sites."
}
end

View File

@ -170,10 +170,7 @@ defmodule PlausibleWeb.SiteControllerTest do
conn: conn,
user: user
} do
# default site limit defined in config/.test.env
insert(:site, members: [user])
insert(:site, members: [user])
insert(:site, members: [user])
insert_list(50, :site, members: [user])
conn =
post(conn, "/sites", %{
@ -185,7 +182,7 @@ defmodule PlausibleWeb.SiteControllerTest do
assert html = html_response(conn, 200)
assert html =~ "Upgrade required"
assert html =~ "Your account is limited to 3 sites"
assert html =~ "Your account is limited to 50 sites"
assert html =~ "Please contact support"
refute Repo.get_by(Plausible.Site, domain: "over-limit.example.com")
end