591 lines
18 KiB
Elixir
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
|