defmodule FeedPug.PromEx.Plugins.LibraryTest do
@moduledoc """
The plugin's two pollers run raw SQL against tables no context module owns,
so nothing else in the suite would notice these queries drifting from the
schema. Each test drives the poll function directly and reads the telemetry
it emits.
"""
use FeedPug.DataCase, async: true
import FeedPug.AccountsFixtures
alias FeedPug.Feeds
alias FeedPug.Groups
alias FeedPug.PromEx.Plugins.Library
@events [
[:feed_pug, :library, :users],
[:feed_pug, :library, :feeds],
[:feed_pug, :library, :following],
[:feed_pug, :library, :rows, :estimate],
[:feed_pug, :library, :corpus]
]
setup do
ref = make_ref()
test = self()
:telemetry.attach_many(
"library-test-#{inspect(ref)}",
@events,
fn event, measurements, metadata, _ ->
send(test, {:metric, event, measurements, metadata})
end,
nil
)
on_exit(fn -> :telemetry.detach("library-test-#{inspect(ref)}") end)
:ok
end
# Every emission for one event, as {metadata, measurements} pairs. Drains the
# mailbox rather than matching one message, because the zero-filled metrics
# emit once per label value.
defp collect(event) do
receive do
{:metric, ^event, measurements, metadata} -> [{metadata, measurements} | collect(event)]
after
0 -> []
end
end
defp single(event) do
Library.execute_library_metrics()
[{_metadata, measurements}] = collect(event)
measurements
end
defp group(scope, path) do
Enum.find(Groups.list_groups(scope), &(&1.path == path))
end
defp add_feed(scope, path, url) do
{:ok, group_feed} = Groups.add_feed_to_group(scope, group(scope, path), url)
Feeds.get_feed!(group_feed.feed_id)
end
defp add_items(feed, count) do
entries = for n <- 1..count, do: %{guid: "#{feed.id}-#{n}", title: "item #{n}"}
{_n, items} = Feeds.store_items(feed, entries)
items
end
describe "users" do
test "splits on confirmation and reports both states even when one is empty" do
user_fixture()
user_fixture()
Library.execute_library_metrics()
counts =
[:feed_pug, :library, :users]
|> collect()
|> Map.new(fn {%{state: state}, %{users: users}} -> {state, users} end)
assert counts == %{"confirmed" => 2, "pending" => 0}
end
test "counts an unconfirmed registration as pending" do
unconfirmed_user_fixture()
Library.execute_library_metrics()
counts =
[:feed_pug, :library, :users]
|> collect()
|> Map.new(fn {%{state: state}, %{users: users}} -> {state, users} end)
assert counts == %{"confirmed" => 0, "pending" => 1}
end
end
describe "feeds" do
test "reports every status in the schema, not only the ones in use" do
scope = user_scope_fixture()
feed = add_feed(scope, "blogs", "https://a.example/feed.xml")
{:ok, _} = Feeds.update_feed(feed, %{status: "failed"})
Library.execute_library_metrics()
counts =
[:feed_pug, :library, :feeds]
|> collect()
|> Map.new(fn {%{status: status}, %{feeds: feeds}} -> {status, feeds} end)
assert counts == %{"active" => 0, "paused" => 0, "failed" => 1, "archived" => 0}
end
end
describe "following" do
test "separates placements, distinct subscriptions and distinct feeds" do
scope = user_scope_fixture()
other = user_scope_fixture()
# One feed filed under two of the same user's groups: two placements,
# one subscription, one feed. A second user following it adds a
# subscription without adding a feed.
shared = "https://shared.example/feed.xml"
add_feed(scope, "blogs", shared)
add_feed(scope, "news", shared)
add_feed(other, "blogs", shared)
add_feed(scope, "comics", "https://solo.example/feed.xml")
assert %{
placements: 4,
subscriptions: 3,
subscribed_feeds: 2,
group_follows: 0
} = single([:feed_pug, :library, :following])
end
test "counts a follow of another user's group" do
scope = user_scope_fixture()
other = user_scope_fixture()
{:ok, _follow} = Groups.follow_group(other, group(scope, "blogs"))
assert %{group_follows: 1} = single([:feed_pug, :library, :following])
end
end
describe "corpus and read ratio" do
test "counts items and reads, and divides only over what is subscribed" do
scope = user_scope_fixture()
followed = add_feed(scope, "blogs", "https://followed.example/feed.xml")
# A feed nobody has in a group: its items are stored and fetched, and
# they are not readable by anyone.
{:ok, orphan} = Feeds.upsert_feed_by_url("https://orphan.example/feed.xml")
[a, b, _c, _d] = add_items(followed, 4)
[e | _] = add_items(orphan, 2)
:ok = Feeds.mark_read(scope.user.id, a.id)
:ok = Feeds.mark_read(scope.user.id, b.id)
# A read left behind on a feed the user does not subscribe to. It counts
# as a read marker and must not count towards the ratio, which would
# otherwise be able to exceed 1.
:ok = Feeds.mark_read(scope.user.id, e.id)
Library.execute_corpus_metrics()
assert [{_meta, measurements}] = collect([:feed_pug, :library, :corpus])
assert %{
items: 6,
item_reads: 3,
readable_items: 4,
subscribed_item_reads: 2,
read_ratio: ratio
} = measurements
# A float, not a Decimal: `sum()` over a count comes back as numeric, and
# dividing by the Decimal that produces raises instead.
assert is_float(ratio)
assert ratio == 0.5
end
test "counts the same item once per subscriber" do
scope = user_scope_fixture()
other = user_scope_fixture()
shared = "https://shared.example/feed.xml"
feed = add_feed(scope, "blogs", shared)
add_feed(other, "blogs", shared)
[a, _b] = add_items(feed, 2)
:ok = Feeds.mark_read(scope.user.id, a.id)
Library.execute_corpus_metrics()
[{_meta, measurements}] = collect([:feed_pug, :library, :corpus])
assert %{items: 2, readable_items: 4, subscribed_item_reads: 1, read_ratio: 0.25} =
measurements
end
test "reports a ratio of zero rather than dividing by nothing" do
Library.execute_corpus_metrics()
[{_meta, measurements}] = collect([:feed_pug, :library, :corpus])
assert %{items: 0, item_reads: 0, readable_items: 0, read_ratio: +0.0} = measurements
end
end
describe "row estimates" do
test "reports both unbounded tables on every poll" do
Library.execute_library_metrics()
tables =
[:feed_pug, :library, :rows, :estimate]
|> collect()
|> Enum.map(fn {%{table: table}, _} -> table end)
|> Enum.sort()
assert tables == ["item_reads", "items"]
end
end
end
neiam
/feedpug
feedpub
public · Issues · Pulls · Labels · Forks · Compare · Actions success · Badges · Packages
⭐
Log in to mark this repository.
6.9 KiB · text
History
6d24830