analytics/test/plausible/site/sites_test.exs

591 lines
18 KiB
Elixir

defmodule Plausible.SitesTest do
use Plausible.DataCase
alias Plausible.Sites
describe "create a site" do
test "creates a site" do
user = insert(:user)
params = %{"domain" => "example.com", "timezone" => "Europe/London"}
assert {:ok, %{site: %{domain: "example.com", timezone: "Europe/London"}}} =
Sites.create(user, params)
end
test "fails on invalid timezone" do
user = insert(:user)
params = %{"domain" => "example.com", "timezone" => "blah"}
assert {:error, :site, %{errors: [timezone: {"is invalid", []}]}, %{}} =
Sites.create(user, params)
end
end
describe "is_member?" do
test "is true if user is a member of the site" do
user = insert(:user)
site = insert(:site, members: [user])
assert Sites.is_member?(user.id, site)
end
test "is false if user is not a member" do
user = insert(:user)
site = insert(:site)
refute Sites.is_member?(user.id, site)
end
end
describe "stats_start_date" do
test "is nil if site has no stats" do
site = insert(:site)
assert Sites.stats_start_date(site) == nil
end
test "is date if site does have stats" do
site = insert(:site)
populate_stats(site, [
build(:pageview)
])
assert Sites.stats_start_date(site) == Timex.today(site.timezone)
end
test "memoizes value of start date" do
site = insert(:site)
assert site.stats_start_date == nil
populate_stats(site, [
build(:pageview)
])
assert Sites.stats_start_date(site) == Timex.today(site.timezone)
assert Repo.reload!(site).stats_start_date == Timex.today(site.timezone)
end
end
describe "native_stats_start_date" do
test "is nil if site has no stats" do
site = insert(:site)
assert Sites.native_stats_start_date(site) == nil
end
test "is date if site does have stats" do
site = insert(:site)
populate_stats(site, [
build(:pageview)
])
assert Sites.native_stats_start_date(site) == Timex.today(site.timezone)
end
test "ignores imported stats" do
site = insert(:site)
insert(:site_import, site: site)
assert Sites.native_stats_start_date(site) == nil
end
end
describe "has_stats?" do
test "is false if site has no stats" do
site = insert(:site)
refute Sites.has_stats?(site)
end
test "is true if site has stats" do
site = insert(:site)
populate_stats(site, [
build(:pageview)
])
assert Sites.has_stats?(site)
end
end
describe "get_for_user/2" do
@tag :ee_only
test "get site for super_admin" do
user1 = insert(:user)
user2 = insert(:user)
patch_env(:super_admin_user_ids, [user2.id])
%{id: site_id, domain: domain} = insert(:site, members: [user1])
assert %{id: ^site_id} = Sites.get_for_user(user1.id, domain)
assert %{id: ^site_id} = Sites.get_for_user(user1.id, domain, [:owner])
assert is_nil(Sites.get_for_user(user2.id, domain))
assert %{id: ^site_id} = Sites.get_for_user(user2.id, domain, [:super_admin])
end
end
describe "list/3 and list_with_invitations/3" do
test "returns empty when there are no sites" do
user = insert(:user)
_rogue_site = insert(:site)
assert %{
entries: [],
page_size: 24,
page_number: 1,
total_entries: 0,
total_pages: 1
} = Sites.list(user, %{})
assert %{
entries: [],
page_size: 24,
page_number: 1,
total_entries: 0,
total_pages: 1
} = Sites.list_with_invitations(user, %{})
end
test "pinned site doesn't matter with membership revoked (no active invitations)" do
user1 = insert(:user, email: "user1@example.com")
user2 = insert(:user, email: "user2@example.com")
insert(:site, members: [user1], domain: "one.example.com")
site2 =
insert(:site,
members: [user2],
domain: "two.example.com"
)
membership = insert(:site_membership, user: user1, role: :viewer, site: site2)
{:ok, _} = Sites.toggle_pin(user1, site2)
Repo.delete!(membership)
assert %{entries: [%{domain: "one.example.com"}]} = Sites.list(user1, %{})
assert %{entries: [%{domain: "one.example.com"}]} = Sites.list_with_invitations(user1, %{})
end
test "pinned site doesn't matter with membership revoked (with active invitation)" do
user1 = insert(:user, email: "user1@example.com")
user2 = insert(:user, email: "user2@example.com")
insert(:site, members: [user1], domain: "one.example.com")
site2 =
insert(:site,
members: [user2],
domain: "two.example.com"
)
membership = insert(:site_membership, user: user1, role: :viewer, site: site2)
insert(:invitation, email: user1.email, inviter: user2, role: :owner, site: site2)
{:ok, _} = Sites.toggle_pin(user1, site2)
Repo.delete!(membership)
assert %{entries: [%{domain: "one.example.com"}]} = Sites.list(user1, %{})
assert %{entries: [%{domain: "two.example.com"}, %{domain: "one.example.com"}]} =
Sites.list_with_invitations(user1, %{})
end
test "puts invitations first, pinned sites second, sites last" do
user = insert(:user, email: "hello@example.com")
site1 = %{id: site_id1} = insert(:site, members: [user], domain: "one.example.com")
site2 = %{id: site_id2} = insert(:site, members: [user], domain: "two.example.com")
%{id: site_id4} = insert(:site, members: [user], domain: "four.example.com")
_rogue_site = insert(:site, domain: "rogue.example.com")
insert(:invitation, email: user.email, inviter: build(:user), role: :owner, site: site1)
%{id: site_id3} =
insert(:site,
domain: "three.example.com",
invitations: [
build(:invitation, email: user.email, inviter: build(:user), role: :viewer)
]
)
insert(:invitation, email: "friend@example.com", inviter: user, role: :viewer, site: site1)
insert(:invitation,
site: site1,
inviter: user,
email: "another@example.com"
)
{:ok, _} = Sites.toggle_pin(user, site2)
assert %{
entries: [
%{id: ^site_id2, entry_type: "pinned_site"},
%{id: ^site_id4, entry_type: "site"},
%{id: ^site_id1, entry_type: "site"}
]
} = Sites.list(user, %{})
assert %{
entries: [
%{id: ^site_id1, entry_type: "invitation"},
%{id: ^site_id3, entry_type: "invitation"},
%{id: ^site_id2, entry_type: "pinned_site"},
%{id: ^site_id4, entry_type: "site"}
]
} = Sites.list_with_invitations(user, %{})
end
test "pinned sites are ordered according to the time they were pinned at" do
user = insert(:user, email: "hello@example.com")
site1 = %{id: site_id1} = insert(:site, members: [user], domain: "one.example.com")
site2 = %{id: site_id2} = insert(:site, members: [user], domain: "two.example.com")
site4 = %{id: site_id4} = insert(:site, members: [user], domain: "four.example.com")
_rogue_site = insert(:site, domain: "rogue.example.com")
insert(:invitation, email: user.email, inviter: build(:user), role: :owner, site: site1)
%{id: site_id3} =
insert(:site,
domain: "three.example.com",
invitations: [
build(:invitation, email: user.email, inviter: build(:user), role: :viewer)
]
)
insert(:invitation, email: "friend@example.com", inviter: user, role: :viewer, site: site1)
insert(:invitation,
site: site1,
inviter: user,
email: "another@example.com"
)
Sites.set_option(user, site2, :pinned_at, ~N[2023-10-22 12:00:00])
{:ok, _} = Sites.toggle_pin(user, site4)
assert %{
entries: [
%{id: ^site_id4, entry_type: "pinned_site"},
%{id: ^site_id2, entry_type: "pinned_site"},
%{id: ^site_id1, entry_type: "site"}
]
} = Sites.list(user, %{})
assert %{
entries: [
%{id: ^site_id1, entry_type: "invitation"},
%{id: ^site_id3, entry_type: "invitation"},
%{id: ^site_id4, entry_type: "pinned_site"},
%{id: ^site_id2, entry_type: "pinned_site"}
]
} = Sites.list_with_invitations(user, %{})
end
test "filters by domain" do
user = insert(:user)
%{id: site_id1} = insert(:site, domain: "first.example.com", members: [user])
%{id: _site_id2} = insert(:site, domain: "second.example.com", members: [user])
_rogue_site = insert(:site)
%{id: site_id3} =
insert(:site,
domain: "first-another.example.com",
invitations: [
build(:invitation, email: user.email, inviter: build(:user), role: :viewer)
]
)
assert %{
entries: [
%{id: ^site_id1}
]
} = Sites.list(user, %{}, filter_by_domain: "first")
assert %{
entries: [
%{id: ^site_id3},
%{id: ^site_id1}
]
} = Sites.list_with_invitations(user, %{}, filter_by_domain: "first")
end
end
describe "list/3" do
test "returns sites only, no invitations" do
user = insert(:user, email: "hello@example.com")
site1 = %{id: site_id1} = insert(:site, members: [user], domain: "one.example.com")
%{id: site_id2} = insert(:site, members: [user], domain: "two.example.com")
%{id: site_id4} = insert(:site, members: [user], domain: "four.example.com")
_rogue_site = insert(:site, domain: "rogue.example.com")
insert(:invitation, email: user.email, inviter: build(:user), role: :owner, site: site1)
insert(:site,
domain: "three.example.com",
invitations: [
build(:invitation, email: user.email, inviter: build(:user), role: :viewer)
]
)
insert(:invitation, email: "friend@example.com", inviter: user, role: :viewer, site: site1)
insert(:invitation,
site: site1,
inviter: user,
email: "another@example.com"
)
assert %{
entries: [
%{id: ^site_id4, entry_type: "site"},
%{id: ^site_id1, entry_type: "site"},
%{id: ^site_id2, entry_type: "site"}
]
} = Sites.list(user, %{})
end
test "handles pagination correctly" do
user = insert(:user)
%{id: site_id1} = insert(:site, members: [user])
%{id: site_id2} = insert(:site, members: [user])
_rogue_site = insert(:site)
insert(:site,
invitations: [
build(:invitation, email: user.email, inviter: build(:user), role: :viewer)
]
)
site4 = %{id: site_id4} = insert(:site, members: [user])
{:ok, _} = Sites.toggle_pin(user, site4)
assert %{
entries: [
%{id: ^site_id4},
%{id: ^site_id1}
],
page_number: 1,
page_size: 2,
total_entries: 3,
total_pages: 2
} = Sites.list(user, %{"page_size" => 2})
assert %{
entries: [
%{id: ^site_id2}
],
page_number: 2,
page_size: 2,
total_entries: 3,
total_pages: 2
} = Sites.list(user, %{"page" => 2, "page_size" => 2})
assert %{
entries: [
%{id: ^site_id4},
%{id: ^site_id1}
],
page_number: 1,
page_size: 2,
total_entries: 3,
total_pages: 2
} = Sites.list(user, %{"page" => 1, "page_size" => 2})
end
end
describe "list_with_invitations/3" do
test "returns invitations and sites" do
user = insert(:user, email: "hello@example.com")
site1 = %{id: site_id1} = insert(:site, members: [user], domain: "one.example.com")
%{id: site_id2} = insert(:site, members: [user], domain: "two.example.com")
%{id: site_id4} = insert(:site, members: [user], domain: "four.example.com")
_rogue_site = insert(:site, domain: "rogue.example.com")
insert(:invitation, email: user.email, inviter: build(:user), role: :owner, site: site1)
%{id: site_id3} =
insert(:site,
domain: "three.example.com",
invitations: [
build(:invitation, email: user.email, inviter: build(:user), role: :viewer)
]
)
insert(:invitation, email: "friend@example.com", inviter: user, role: :viewer, site: site1)
insert(:invitation,
site: site1,
inviter: user,
email: "another@example.com"
)
assert %{
entries: [
%{id: ^site_id1, entry_type: "invitation"},
%{id: ^site_id3, entry_type: "invitation"},
%{id: ^site_id4, entry_type: "site"},
%{id: ^site_id2, entry_type: "site"}
]
} = Sites.list_with_invitations(user, %{})
end
test "handles pagination correctly" do
user = insert(:user)
%{id: site_id1} = insert(:site, members: [user])
%{id: site_id2} = insert(:site, members: [user])
_rogue_site = insert(:site)
%{id: site_id3} =
insert(:site,
invitations: [
build(:invitation, email: user.email, inviter: build(:user), role: :viewer)
]
)
assert %{
entries: [
%{id: ^site_id3},
%{id: ^site_id1}
],
page_number: 1,
page_size: 2,
total_entries: 3,
total_pages: 2
} = Sites.list_with_invitations(user, %{"page_size" => 2})
assert %{
entries: [
%{id: ^site_id2}
],
page_number: 2,
page_size: 2,
total_entries: 3,
total_pages: 2
} = Sites.list_with_invitations(user, %{"page" => 2, "page_size" => 2})
assert %{
entries: [
%{id: ^site_id3},
%{id: ^site_id1}
],
page_number: 1,
page_size: 2,
total_entries: 3,
total_pages: 2
} = Sites.list_with_invitations(user, %{"page" => 1, "page_size" => 2})
end
end
describe "set_option/4" do
test "allows setting option multiple times" do
user = insert(:user)
site = insert(:site, members: [user])
assert prefs =
%{pinned_at: %NaiveDateTime{}} =
Sites.set_option(user, site, :pinned_at, NaiveDateTime.utc_now())
prefs = Repo.reload!(prefs)
assert prefs.site_id == site.id
assert prefs.user_id == user.id
assert prefs.pinned_at
assert prefs =
%{pinned_at: nil} = Sites.set_option(user, site, :pinned_at, nil)
prefs = Repo.reload!(prefs)
assert prefs.site_id == site.id
assert prefs.user_id == user.id
refute prefs.pinned_at
assert prefs =
%{pinned_at: %NaiveDateTime{}} =
Sites.set_option(user, site, :pinned_at, NaiveDateTime.utc_now())
prefs = Repo.reload!(prefs)
assert prefs.site_id == site.id
assert prefs.user_id == user.id
assert prefs.pinned_at
end
test "raises on invalid option" do
user = insert(:user)
site = insert(:site, members: [user])
assert_raise FunctionClauseError, fn ->
Sites.set_option(user, site, :invalid, false)
end
end
test "raises on invalid site/user combination" do
user = insert(:user)
site = insert(:site)
assert_raise Ecto.NoResultsError, fn ->
Sites.set_option(user, site, :pinned_at, nil)
end
end
end
describe "toggle_pin/2" do
test "allows pinning and unpinning site" do
user = insert(:user)
site = insert(:site, members: [user])
site = %{site | pinned_at: nil}
assert {:ok, prefs} = Sites.toggle_pin(user, site)
assert prefs = %{pinned_at: %NaiveDateTime{}} = prefs
prefs = Repo.reload!(prefs)
assert prefs.site_id == site.id
assert prefs.user_id == user.id
assert prefs.pinned_at
site = %{site | pinned_at: NaiveDateTime.utc_now()}
assert {:ok, prefs} = Sites.toggle_pin(user, site)
assert %{pinned_at: nil} = prefs
prefs = Repo.reload!(prefs)
assert prefs.site_id == site.id
assert prefs.user_id == user.id
refute prefs.pinned_at
site = %{site | pinned_at: nil}
assert {:ok, prefs} = Sites.toggle_pin(user, site)
assert %{pinned_at: %NaiveDateTime{}} = prefs
prefs = Repo.reload!(prefs)
assert prefs.site_id == site.id
assert prefs.user_id == user.id
assert prefs.pinned_at
end
test "returns error when pins limit hit" do
user = insert(:user)
for _ <- 1..9 do
site = insert(:site, members: [user])
assert {:ok, _} = Sites.toggle_pin(user, site)
end
site = insert(:site, members: [user])
assert {:error, :too_many_pins} = Sites.toggle_pin(user, site)
end
end
end