berrypod/test/berrypod_web/plugs/analytics_test.exs
jamey 162a5bfe9a
All checks were successful
deploy / deploy (push) Successful in 1m13s
replace analytics double-count prevention with buffer supersede
The Plug records a pageview with a known ID (plug_ref) into the ETS
buffer. When JS connects, the LiveView hook supersedes that event by
ID and records its own with full data (screen_size from connect params).
If JS never connects, the Plug's event flushes normally after 10s.

Also fixes: admin browsing no longer leaks product_view events — the
Plug now sets no analytics session data for admins, so all downstream
visitor_hash guards naturally filter them out.

Replaces the previous time-based skip logic which was brittle and
race-prone. The supersede approach is deterministic and handles both
the ETS buffer and already-flushed DB cases.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 14:48:50 +00:00

126 lines
3.1 KiB
Elixir

defmodule BerrypodWeb.Plugs.AnalyticsTest do
use BerrypodWeb.ConnCase, async: false
import Ecto.Query
alias Berrypod.Analytics.{Buffer, Event}
alias Berrypod.Repo
setup do
send(Buffer, :flush)
:timer.sleep(50)
Repo.delete_all(Event)
:ok
end
describe "analytics plug" do
test "records a pageview on GET request", %{conn: conn} do
conn
|> put_req_header("user-agent", "Mozilla/5.0 Chrome/120.0")
|> get(~p"/")
send(Buffer, :flush)
:timer.sleep(50)
events =
from(e in Event, where: e.pathname == "/" and e.name == "pageview")
|> Repo.all()
assert length(events) >= 1
event = hd(events)
assert event.browser == "Chrome"
end
test "does not record on POST request", %{conn: conn} do
count_before = Repo.aggregate(Event, :count)
conn
|> put_req_header("user-agent", "Mozilla/5.0 Chrome/120.0")
|> post(~p"/checkout", %{})
send(Buffer, :flush)
:timer.sleep(50)
count_after = Repo.aggregate(Event, :count)
assert count_after == count_before
end
test "skips bots", %{conn: conn} do
count_before = Repo.aggregate(Event, :count)
conn
|> put_req_header("user-agent", "Googlebot/2.1 (+http://www.google.com/bot.html)")
|> get(~p"/")
send(Buffer, :flush)
:timer.sleep(50)
count_after = Repo.aggregate(Event, :count)
assert count_after == count_before
end
test "stores analytics data in session", %{conn: conn} do
conn =
conn
|> put_req_header("user-agent", "Mozilla/5.0 Firefox/121.0")
|> get(~p"/")
assert get_session(conn, "analytics_visitor_hash") |> is_binary()
assert get_session(conn, "analytics_browser") == "Firefox"
end
test "stores plug_ref in session for buffer supersede", %{conn: conn} do
conn =
conn
|> put_req_header("user-agent", "Mozilla/5.0 Chrome/120.0")
|> get(~p"/")
plug_ref = get_session(conn, "analytics_plug_ref")
assert is_binary(plug_ref)
assert String.length(plug_ref) == 36
end
test "extracts referrer", %{conn: conn} do
conn
|> put_req_header("user-agent", "Mozilla/5.0 Chrome/120.0")
|> put_req_header("referer", "https://www.google.com/search?q=test")
|> get(~p"/")
send(Buffer, :flush)
:timer.sleep(50)
event =
from(e in Event,
where: e.referrer == "google.com",
order_by: [desc: e.inserted_at],
limit: 1
)
|> Repo.one()
assert event
assert event.referrer_source == "Google"
end
test "extracts UTM params", %{conn: conn} do
conn
|> put_req_header("user-agent", "Mozilla/5.0 Chrome/120.0")
|> get(~p"/?utm_source=newsletter&utm_medium=email&utm_campaign=spring")
send(Buffer, :flush)
:timer.sleep(50)
event =
from(e in Event,
where: e.utm_source == "newsletter",
order_by: [desc: e.inserted_at],
limit: 1
)
|> Repo.one()
assert event
assert event.utm_medium == "email"
assert event.utm_campaign == "spring"
end
end
end