analytics/test/workers/traffic_change_notifier_tes...

505 lines
15 KiB
Elixir

defmodule Plausible.Workers.TrafficChangeNotifierTest do
use Plausible.DataCase, async: true
use Bamboo.Test
alias Plausible.Workers.TrafficChangeNotifier
@view_dashboard_text "View dashboard"
@review_installation_text "review your installation"
describe "drops" do
test "does not notify anyone if we've stopped accepting traffic for the owner" do
user = new_user(team: [accept_traffic_until: Date.utc_today()])
site = new_site(owner: user)
insert(:drop_notification,
site: site,
threshold: 10,
recipients: ["jerod@example.com", "uku@example.com"]
)
TrafficChangeNotifier.perform(nil)
assert_no_emails_delivered()
end
test "does notify if threshold reached and we're accepting traffic" do
user = new_user(team: [accept_traffic_until: Date.utc_today() |> Date.add(+1)])
site = new_site(owner: user)
insert(:drop_notification,
site: site,
threshold: 10,
recipients: ["jerod@example.com"]
)
TrafficChangeNotifier.perform(nil)
assert_email_delivered_with(
subject: "Traffic drop on #{site.domain}",
to: [nil: "jerod@example.com"]
)
end
test "does not notify anyone if current visitors does not drop below notification threshold" do
site = insert(:site)
insert(:drop_notification,
site: site,
threshold: 2,
recipients: ["jerod@example.com", "uku@example.com"]
)
populate_stats(site, [
build(:pageview, timestamp: minutes_ago(2)),
build(:pageview, timestamp: minutes_ago(1))
])
TrafficChangeNotifier.perform(nil)
assert_no_emails_delivered()
end
test "notifies all recipients when traffic drops under configured threshold" do
site = new_site()
insert(:drop_notification,
site: site,
threshold: 10,
recipients: ["jerod@example.com", "uku@example.com"]
)
TrafficChangeNotifier.perform(nil)
assert_email_delivered_with(
subject: "Traffic drop on #{site.domain}",
to: [nil: "jerod@example.com"]
)
assert_email_delivered_with(
subject: "Traffic drop on #{site.domain}",
to: [nil: "uku@example.com"]
)
end
test "includes dashboard and installation links only when recipient is guest or team member" do
owner = new_user()
{:ok, team} = Plausible.Teams.get_or_create(owner)
site = new_site(team: team)
team_member = add_member(team, role: :admin)
viewer_guest = add_guest(site, role: :viewer, user: new_user())
random_email = "random@example.com"
insert(:drop_notification,
site: site,
threshold: 10,
recipients: [owner.email, team_member.email, viewer_guest.email, random_email]
)
TrafficChangeNotifier.perform(nil)
expected_subject = "Traffic drop on #{site.domain}"
four_emails =
for _ <- 1..4 do
assert_delivered_email_matches(%{
html_body: html_body,
subject: ^expected_subject,
to: [nil: email]
})
%{to: email, html_body: html_body}
end
{[random_recipient_email], site_member_emails} =
Enum.split_with(four_emails, &(&1.to == random_email))
refute random_recipient_email.html_body =~ @view_dashboard_text
Enum.each(site_member_emails, fn email ->
assert email.html_body =~ @view_dashboard_text
assert email.html_body =~ @review_installation_text
end)
end
on_ee do
test "does not link to site installation in a consolidated view traffic drop notification" do
%{email: user_email} = user = new_user()
{:ok, team} = Plausible.Teams.get_or_create(user)
new_site(team: team)
new_site(team: team)
consolidated_view = new_consolidated_view(team)
insert(:drop_notification,
site: consolidated_view,
threshold: 10,
recipients: [user.email]
)
TrafficChangeNotifier.perform(nil)
assert_delivered_email_matches(%{
html_body: html_body,
subject: "Traffic drop on your sites",
to: [nil: ^user_email]
})
assert html_body =~ "across all your sites"
assert html_body =~ @view_dashboard_text
refute html_body =~ @review_installation_text
end
test "does not notify traffic drop on consolidated view when ok_to_display? is false" do
user = new_user()
{:ok, team} = Plausible.Teams.get_or_create(user)
site = new_site(team: team)
new_site(team: team)
consolidated_view = new_consolidated_view(team)
Plausible.Repo.delete(site)
refute Plausible.ConsolidatedView.ok_to_display?(team)
insert(:drop_notification,
site: consolidated_view,
threshold: 10,
recipients: [user.email]
)
TrafficChangeNotifier.perform(nil)
assert_no_emails_delivered()
end
end
test "does not send notifications more than once every 12 hours" do
site = new_site()
insert(:drop_notification,
site: site,
threshold: 1,
recipients: ["uku@example.com"]
)
TrafficChangeNotifier.perform(nil, ~N[2021-01-01 00:00:00])
assert_email_delivered_with(
subject: "Traffic drop on #{site.domain}",
to: [nil: "uku@example.com"]
)
TrafficChangeNotifier.perform(nil, ~N[2021-01-01 11:59:59])
assert_no_emails_delivered()
TrafficChangeNotifier.perform(nil, ~N[2021-01-01 12:00:01])
assert_email_delivered_with(
subject: "Traffic drop on #{site.domain}",
to: [nil: "uku@example.com"]
)
end
test "adds settings link if recipient has access to the site" do
user = new_user(email: "robert@example.com")
site = new_site(domain: "example.com", owner: user)
insert(:drop_notification,
site: site,
threshold: 10,
recipients: ["robert@example.com"]
)
TrafficChangeNotifier.perform(nil)
assert_email_delivered_with(
html_body: ~r|http://localhost:8000/example.com/installation\?flow=review|
)
end
end
describe "spikes" do
test "does not notify anyone if current visitors does not exceed notification threshold" do
site = insert(:site)
insert(:spike_notification,
site: site,
threshold: 2,
recipients: ["jerod@example.com", "uku@example.com"]
)
populate_stats(site, [build(:pageview, timestamp: minutes_ago(1))])
TrafficChangeNotifier.perform(nil)
assert_no_emails_delivered()
end
test "notifies all recipients when traffic is higher than configured threshold" do
site = new_site()
insert(:spike_notification,
site: site,
threshold: 2,
recipients: ["jerod@example.com", "uku@example.com"]
)
populate_stats(site, [
build(:pageview, timestamp: minutes_ago(3)),
build(:pageview, timestamp: minutes_ago(2)),
build(:pageview, timestamp: minutes_ago(1))
])
TrafficChangeNotifier.perform(nil)
assert_email_delivered_with(
subject: "Traffic spike on #{site.domain}",
to: [nil: "jerod@example.com"]
)
assert_email_delivered_with(
subject: "Traffic spike on #{site.domain}",
to: [nil: "uku@example.com"]
)
end
on_ee do
test "notifies traffic spike on consolidated view" do
{:ok, team} = new_user() |> Plausible.Teams.get_or_create()
site1 = new_site(team: team)
site2 = new_site(team: team)
consolidated_view = new_consolidated_view(team)
insert(:spike_notification,
site: consolidated_view,
threshold: 2,
recipients: ["uku@example.com"]
)
populate_stats(site1, [
build(:pageview, referrer_source: "Google", pathname: "/b", timestamp: minutes_ago(1)),
build(:pageview, referrer_source: "Google", pathname: "/a", timestamp: minutes_ago(2))
])
populate_stats(site2, [
build(:pageview, referrer_source: "Twitter", pathname: "/a", timestamp: minutes_ago(3))
])
TrafficChangeNotifier.perform(nil)
assert_delivered_email_matches(%{
html_body: html_body,
subject: "Traffic spike on your sites",
to: [nil: "uku@example.com"]
})
assert html_body =~ "across all your sites"
assert html_body =~ "The top sources for current visitors:"
assert html_body =~ "<b>2</b> visitors from <b>Google</b>"
assert html_body =~ "<b>1</b> visitor from <b>Twitter</b>"
assert html_body =~ "Your top pages being visited:"
assert html_body =~ "<b>2</b> visitors on <b>/a</b>"
assert html_body =~ "<b>1</b> visitor on <b>/b</b>"
end
test "does not notify traffic spike on consolidated view when ok_to_display? is false" do
user = new_user()
{:ok, team} = Plausible.Teams.get_or_create(user)
site1 = new_site(team: team)
site2 = new_site(team: team)
consolidated_view = new_consolidated_view(team)
insert(:spike_notification,
site: consolidated_view,
threshold: 2,
recipients: ["uku@example.com"]
)
populate_stats(site1, [
build(:pageview, timestamp: minutes_ago(1)),
build(:pageview, timestamp: minutes_ago(2)),
build(:pageview, timestamp: minutes_ago(3))
])
Plausible.Repo.delete(site2)
refute Plausible.ConsolidatedView.ok_to_display?(team)
insert(:drop_notification,
site: consolidated_view,
threshold: 10,
recipients: [user.email]
)
TrafficChangeNotifier.perform(nil)
assert_no_emails_delivered()
end
end
test "ignores 'Direct / None' source" do
site = new_site()
insert(:spike_notification,
site: site,
threshold: 5,
recipients: ["uku@example.com"]
)
populate_stats(site, [
build(:pageview, referrer_source: "A", timestamp: minutes_ago(1)),
build(:pageview, referrer_source: "A", timestamp: minutes_ago(1)),
build(:pageview, referrer_source: "A", timestamp: minutes_ago(1)),
build(:pageview, referrer_source: "B", timestamp: minutes_ago(1)),
build(:pageview, referrer_source: "B", timestamp: minutes_ago(1)),
build(:pageview, referrer_source: "C", timestamp: minutes_ago(1)),
build(:pageview, timestamp: minutes_ago(1)),
build(:pageview, timestamp: minutes_ago(1))
])
TrafficChangeNotifier.perform(nil)
assert_delivered_email_matches(%{
to: [nil: "uku@example.com"],
html_body: html_body
})
assert html_body =~ "The top sources for current visitors:"
assert html_body =~ "<b>3</b> visitors from <b>A</b>"
assert html_body =~ "<b>2</b> visitors from <b>B</b>"
assert html_body =~ "<b>1</b> visitor from <b>C</b>"
assert html_body =~ "There are currently <b>8</b>"
end
test "does not list sources at all when everything is 'Direct / None'" do
site = new_site()
insert(:spike_notification,
site: site,
threshold: 1,
recipients: ["uku@example.com"]
)
populate_stats(site, [
build(:pageview, timestamp: minutes_ago(1)),
build(:pageview, timestamp: minutes_ago(1))
])
TrafficChangeNotifier.perform(nil)
assert_delivered_email_matches(%{
to: [nil: "uku@example.com"],
html_body: html_body
})
refute html_body =~ "The top sources for current visitors:"
assert html_body =~ "There are currently <b>2</b>"
end
test "includes top 3 pages" do
site = new_site()
insert(:spike_notification,
site: site,
threshold: 10,
recipients: ["uku@example.com"]
)
populate_stats(site, [
build(:pageview, pathname: "/one", timestamp: minutes_ago(1)),
build(:pageview, pathname: "/one", timestamp: minutes_ago(1)),
build(:pageview, pathname: "/one", timestamp: minutes_ago(1)),
build(:pageview, pathname: "/one", timestamp: minutes_ago(1)),
build(:pageview, pathname: "/two", timestamp: minutes_ago(1)),
build(:pageview, pathname: "/two", timestamp: minutes_ago(1)),
build(:pageview, pathname: "/two", timestamp: minutes_ago(1)),
build(:pageview, timestamp: minutes_ago(1)),
build(:pageview, timestamp: minutes_ago(1)),
build(:pageview, pathname: "/not-this-one", timestamp: minutes_ago(1))
])
TrafficChangeNotifier.perform(nil)
assert_delivered_email_matches(%{
to: [nil: "uku@example.com"],
html_body: html_body
})
assert html_body =~ "There are currently <b>10</b>"
assert html_body =~ "Your top pages being visited:"
assert html_body =~ "<b>4</b> visitors on <b>/one</b>"
assert html_body =~ "<b>3</b> visitors on <b>/two</b>"
assert html_body =~ "<b>2</b> visitors on <b>/</b>"
refute html_body =~ "/not-this-one"
end
test "does not check site if it is locked" do
site = new_site()
site.team |> Ecto.Changeset.change(locked: true) |> Repo.update!()
insert(:spike_notification,
site: site,
threshold: 1,
recipients: ["uku@example.com"]
)
populate_stats(site, [
build(:pageview, timestamp: minutes_ago(1)),
build(:pageview, timestamp: minutes_ago(1))
])
TrafficChangeNotifier.perform(nil)
assert_no_emails_delivered()
end
test "does not send notifications more than once every 12 hours" do
site = new_site()
insert(:spike_notification, site: site, threshold: 1, recipients: ["uku@example.com"])
populate_stats(site, [
build(:pageview, timestamp: minutes_ago(1)),
build(:pageview, timestamp: minutes_ago(1))
])
TrafficChangeNotifier.perform(nil)
assert_email_delivered_with(
subject: "Traffic spike on #{site.domain}",
to: [nil: "uku@example.com"]
)
TrafficChangeNotifier.perform(nil)
assert_no_emails_delivered()
end
test "adds a dashboard link if recipient has access to the site" do
user = new_user(email: "robert@example.com")
site = new_site(domain: "example.com", owner: user)
insert(:spike_notification, site: site, threshold: 1, recipients: ["robert@example.com"])
populate_stats(site, [
build(:pageview, timestamp: minutes_ago(1)),
build(:pageview, timestamp: minutes_ago(1))
])
TrafficChangeNotifier.perform(nil)
assert_delivered_email_matches(%{html_body: html_body})
assert html_body =~ @view_dashboard_text
end
end
def minutes_ago(min) do
NaiveDateTime.utc_now(:second) |> NaiveDateTime.add(-min, :minute)
end
end