defmodule Plausible.Workers.CheckUsageTest do use Plausible.DataCase, async: true use Bamboo.Test import Double alias Plausible.Workers.CheckUsage require Plausible.Billing.Subscription.Status setup [:create_user, :create_site] @paddle_id_10k "558018" @date_range Date.range(Date.utc_today(), Date.utc_today()) @accepted_status_values [ Plausible.Billing.Subscription.Status.active(), Plausible.Billing.Subscription.Status.past_due(), Plausible.Billing.Subscription.Status.deleted() ] test "ignores user without subscription" do CheckUsage.perform(nil) assert_no_emails_delivered() end test "operates on the current subscription", %{ user: user } do usage_stub = Plausible.Teams.Billing |> stub(:monthly_pageview_usage, fn _user -> %{ penultimate_cycle: %{date_range: @date_range, total: 11_000}, last_cycle: %{date_range: @date_range, total: 11_000} } end) subscribe_to_plan( user, @paddle_id_10k, last_bill_date: Date.shift(Date.utc_today(), day: -1), status: :active ) subscribe_to_plan( user, "wont-exist-should-crash", last_bill_date: Date.shift(Date.utc_today(), day: -1), inserted_at: DateTime.shift(DateTime.utc_now(), day: -2), status: :deleted ) CheckUsage.perform(nil, usage_stub) assert_email_delivered_with( to: [user], subject: "[Action required] You have outgrown your Plausible subscription tier" ) end test "sends more than one email", %{user: user} do usage_stub = Plausible.Teams.Billing |> stub(:monthly_pageview_usage, fn _user -> %{ penultimate_cycle: %{date_range: @date_range, total: 11_000}, last_cycle: %{date_range: @date_range, total: 11_000} } end) user2 = new_user() new_site(owner: user2) for u <- [user, user2] do subscribe_to_plan( u, @paddle_id_10k, last_bill_date: Date.shift(Date.utc_today(), day: -1), next_bill_date: Date.shift(Date.utc_today(), day: +5), status: :active ) end CheckUsage.perform(nil, usage_stub) assert_email_delivered_with( to: [user], subject: "[Action required] You have outgrown your Plausible subscription tier" ) assert_email_delivered_with( to: [user2], subject: "[Action required] You have outgrown your Plausible subscription tier" ) end test "sends emails to billing members if available", %{user: user} do usage_stub = Plausible.Teams.Billing |> stub(:monthly_pageview_usage, fn _user -> %{ penultimate_cycle: %{date_range: @date_range, total: 11_000}, last_cycle: %{date_range: @date_range, total: 11_000} } end) user2 = new_user() user3 = new_user() new_site(owner: user) team = team_of(user) add_member(team, user: user2, role: :billing) add_member(team, user: user3, role: :viewer) subscribe_to_plan( user, @paddle_id_10k, last_bill_date: Date.shift(Date.utc_today(), day: -1), next_bill_date: Date.shift(Date.utc_today(), day: +5), status: :active ) CheckUsage.perform(nil, usage_stub) assert_email_delivered_with( to: [user], subject: "[Action required] You have outgrown your Plausible subscription tier" ) assert_email_delivered_with( to: [user2], subject: "[Action required] You have outgrown your Plausible subscription tier" ) refute_email_delivered_with( to: [user3], subject: "[Action required] You have outgrown your Plausible subscription tier" ) end test "ignores user with paused subscription", %{user: user} do subscribe_to_plan( user, @paddle_id_10k, last_bill_date: Date.shift(Date.utc_today(), day: -1), status: Plausible.Billing.Subscription.Status.paused() ) CheckUsage.perform(nil) assert_no_emails_delivered() end for status <- @accepted_status_values do describe "#{status} subscription, regular customers" do test "ignores user with subscription but no usage", %{user: user} do subscribe_to_plan(user, @paddle_id_10k, last_bill_date: Date.shift(Date.utc_today(), day: -1), status: unquote(status) ) CheckUsage.perform(nil) assert_no_emails_delivered() assert Repo.reload(team_of(user)).grace_period == nil end test "does not send an email if account has been over the limit for one billing month", %{ user: user } do usage_stub = Plausible.Teams.Billing |> stub(:monthly_pageview_usage, fn _user -> %{ penultimate_cycle: %{date_range: @date_range, total: 9_000}, last_cycle: %{date_range: @date_range, total: 11_000} } end) subscribe_to_plan(user, @paddle_id_10k, last_bill_date: Date.shift(Date.utc_today(), day: -1), status: unquote(status) ) CheckUsage.perform(nil, usage_stub) assert_no_emails_delivered() assert Repo.reload(team_of(user)).grace_period == nil end test "does not send an email if account is over the limit by less than 10%", %{ user: user } do usage_stub = Plausible.Teams.Billing |> stub(:monthly_pageview_usage, fn _user -> %{ penultimate_cycle: %{date_range: @date_range, total: 10_999}, last_cycle: %{date_range: @date_range, total: 11_000} } end) subscribe_to_plan(user, @paddle_id_10k, last_bill_date: Date.shift(Date.utc_today(), day: -1), status: unquote(status) ) CheckUsage.perform(nil, usage_stub) assert_no_emails_delivered() assert Repo.reload(team_of(user)).grace_period == nil end test "sends an email when an account is over their limit for two consecutive billing months", %{ user: user } do usage_stub = Plausible.Teams.Billing |> stub(:monthly_pageview_usage, fn _user -> %{ penultimate_cycle: %{date_range: @date_range, total: 11_000}, last_cycle: %{date_range: @date_range, total: 11_000} } end) subscribe_to_plan( user, @paddle_id_10k, last_bill_date: Date.shift(Date.utc_today(), day: -1), status: unquote(status) ) CheckUsage.perform(nil, usage_stub) assert_email_delivered_with( to: [user], subject: "[Action required] You have outgrown your Plausible subscription tier" ) assert Repo.reload(team_of(user)).grace_period.end_date == Date.shift(Date.utc_today(), day: 7) end test "sends an email suggesting enterprise plan when usage is greater than 10M ", %{ user: user } do usage_stub = Plausible.Teams.Billing |> stub(:monthly_pageview_usage, fn _user -> %{ penultimate_cycle: %{date_range: @date_range, total: 11_000_000}, last_cycle: %{date_range: @date_range, total: 11_000_000} } end) subscribe_to_plan( user, @paddle_id_10k, last_bill_date: Date.shift(Date.utc_today(), day: -1), status: unquote(status) ) CheckUsage.perform(nil, usage_stub) assert_delivered_email_matches(%{html_body: html_body}) assert html_body =~ "Your usage exceeds our standard plans, so please reply back to this email for a tailored quote" end test "skips checking users who already have a grace period", %{user: user} do %{grace_period: existing_grace_period} = Plausible.Teams.start_grace_period(team_of(user)) usage_stub = Plausible.Teams.Billing |> stub(:monthly_pageview_usage, fn _user -> %{ penultimate_cycle: %{date_range: @date_range, total: 11_000}, last_cycle: %{date_range: @date_range, total: 11_000} } end) subscribe_to_plan(user, @paddle_id_10k, last_bill_date: Date.shift(Date.utc_today(), day: -1), status: unquote(status) ) CheckUsage.perform(nil, usage_stub) assert_no_emails_delivered() assert Repo.reload(team_of(user)).grace_period.id == existing_grace_period.id end test "recommends a plan to upgrade to", %{ user: user } do usage_stub = Plausible.Teams.Billing |> stub(:monthly_pageview_usage, fn _user -> %{ penultimate_cycle: %{date_range: @date_range, total: 11_000}, last_cycle: %{date_range: @date_range, total: 11_000} } end) subscribe_to_plan( user, @paddle_id_10k, last_bill_date: Date.shift(Date.utc_today(), day: -1), status: unquote(status) ) CheckUsage.perform(nil, usage_stub) assert_delivered_email_matches(%{ html_body: html_body }) assert html_body =~ "We recommend you upgrade to the 100k pageviews/month plan" end test "clears grace period when plan is applicable again", %{user: user} do usage_stub = Plausible.Teams.Billing |> stub(:monthly_pageview_usage, fn _user -> %{ penultimate_cycle: %{date_range: @date_range, total: 11_000}, last_cycle: %{date_range: @date_range, total: 11_000} } end) subscribe_to_plan( user, @paddle_id_10k, last_bill_date: Date.shift(Date.utc_today(), day: -1), status: unquote(status) ) CheckUsage.perform(nil, usage_stub) assert user |> team_of() |> Repo.reload() |> Plausible.Teams.GracePeriod.active?() usage_stub = Plausible.Teams.Billing |> stub(:monthly_pageview_usage, fn _user -> %{ penultimate_cycle: %{date_range: @date_range, total: 11_000}, last_cycle: %{date_range: @date_range, total: 9_000} } end) CheckUsage.perform(nil, usage_stub) refute user |> team_of() |> Repo.reload() |> Plausible.Teams.GracePeriod.active?() end end end for status <- @accepted_status_values do describe "#{status} subscription, enterprise customers" do test "skips checking enterprise users who already have a grace period", %{user: user} do %{grace_period: existing_grace_period} = Plausible.Teams.start_manual_lock_grace_period(team_of(user)) usage_stub = Plausible.Teams.Billing |> stub(:monthly_pageview_usage, fn _user -> %{ penultimate_cycle: %{date_range: @date_range, total: 1_100_000}, last_cycle: %{date_range: @date_range, total: 1_100_000} } end) subscribe_to_enterprise_plan(user, monthly_pageview_limit: 1_000_000, subscription: [ last_bill_date: Date.shift(Date.utc_today(), day: -1), status: unquote(status) ] ) CheckUsage.perform(nil, usage_stub) assert_no_emails_delivered() assert Repo.reload(team_of(user)).grace_period.id == existing_grace_period.id end test "checks billable pageview usage for enterprise customer, sends usage information to enterprise@plausible.io", %{ user: user } do usage_stub = Plausible.Teams.Billing |> stub(:monthly_pageview_usage, fn _user -> %{ penultimate_cycle: %{date_range: @date_range, total: 1_100_000}, last_cycle: %{date_range: @date_range, total: 1_100_000} } end) subscribe_to_enterprise_plan( user, monthly_pageview_limit: 1_000_000, subscription: [ last_bill_date: Date.shift(Date.utc_today(), day: -1), status: unquote(status) ] ) CheckUsage.perform(nil, usage_stub) assert_email_delivered_with( to: [{nil, "enterprise@plausible.io"}], subject: "#{user.email} has outgrown their enterprise plan" ) end test "will only check usage if enterprise plan matches subscription's paddle plan id", %{ user: user } do usage_stub = Plausible.Teams.Billing |> stub(:monthly_pageview_usage, fn _user -> %{ penultimate_cycle: %{date_range: @date_range, total: 1_100_000}, last_cycle: %{date_range: @date_range, total: 1_100_000} } end) subscribe_to_enterprise_plan( user, monthly_pageview_limit: 1_000_000, subscription: [ last_bill_date: Date.shift(Date.utc_today(), day: -1), status: unquote(status), # non-matching ID paddle_plan_id: @paddle_id_10k ] ) CheckUsage.perform(nil, usage_stub) refute_email_delivered_with( to: [{nil, "enterprise@plausible.io"}], subject: "#{user.email} has outgrown their enterprise plan" ) end test "checks site limit for enterprise customer, sends usage information to enterprise@plausible.io", %{ user: user } do usage_stub = Plausible.Teams.Billing |> stub(:monthly_pageview_usage, fn _user -> %{ penultimate_cycle: %{date_range: @date_range, total: 1}, last_cycle: %{date_range: @date_range, total: 1} } end) subscribe_to_enterprise_plan(user, site_limit: 2, subscription: [ last_bill_date: Date.shift(Date.utc_today(), day: -1), status: unquote(status) ] ) new_site(owner: user) new_site(owner: user) new_site(owner: user) CheckUsage.perform(nil, usage_stub) assert_email_delivered_with( to: [{nil, "enterprise@plausible.io"}], subject: "#{user.email} has outgrown their enterprise plan" ) end test "starts grace period when plan is outgrown", %{user: user} do usage_stub = Plausible.Teams.Billing |> stub(:monthly_pageview_usage, fn _user -> %{ penultimate_cycle: %{date_range: @date_range, total: 1_100_000}, last_cycle: %{date_range: @date_range, total: 1_100_000} } end) subscribe_to_enterprise_plan( user, monthly_pageview_limit: 1_000_000, subscription: [ last_bill_date: Date.shift(Date.utc_today(), day: -1), status: unquote(status) ] ) CheckUsage.perform(nil, usage_stub) assert user |> team_of() |> Repo.reload() |> Plausible.Teams.GracePeriod.active?() end end end describe "timing" do test "checks usage one day after the last_bill_date", %{ user: user } do usage_stub = Plausible.Teams.Billing |> stub(:monthly_pageview_usage, fn _user -> %{ penultimate_cycle: %{date_range: @date_range, total: 11_000}, last_cycle: %{date_range: @date_range, total: 11_000} } end) subscribe_to_plan( user, @paddle_id_10k, last_bill_date: Date.shift(Date.utc_today(), day: -1) ) CheckUsage.perform(nil, usage_stub) assert_email_delivered_with( to: [user], subject: "[Action required] You have outgrown your Plausible subscription tier" ) end test "does not check exactly one month after last_bill_date", %{ user: user } do usage_stub = Plausible.Teams.Billing |> stub(:monthly_pageview_usage, fn _user -> %{ penultimate_cycle: %{date_range: @date_range, total: 11_000}, last_cycle: %{date_range: @date_range, total: 11_000} } end) subscribe_to_plan(user, @paddle_id_10k, last_bill_date: ~D[2021-03-28]) CheckUsage.perform(nil, usage_stub, ~D[2021-03-28]) assert_no_emails_delivered() end test "for yearly subscriptions, checks usage multiple months + one day after the last_bill_date", %{ user: user } do usage_stub = Plausible.Teams.Billing |> stub(:monthly_pageview_usage, fn _user -> %{ penultimate_cycle: %{date_range: @date_range, total: 11_000}, last_cycle: %{date_range: @date_range, total: 11_000} } end) subscribe_to_plan( user, @paddle_id_10k, last_bill_date: ~D[2021-06-29] ) CheckUsage.perform(nil, usage_stub, ~D[2021-08-30]) assert_email_delivered_with( to: [user], subject: "[Action required] You have outgrown your Plausible subscription tier" ) end end end