0
0
Fork 0
mirror of https://github.com/discourse/discourse.git synced 2026-08-06 13:08:40 +08:00
discourse/spec/models/browser_pageview_session_engagement_daily_rollup_spec.rb
Krzysztof Kotlarek f9c97cba31
DEV: Penalize pageviews with no engagement in CrawlerScorer (#42118)
Previously, `CrawlerScorer` discounted a pageview by 40 points when its
session reported human interaction, so a pageview scored before its
engagement beacon settled could never have that credit applied.

This change inverts the signal to penalize sessions with no measured
interaction, raises `BEACON_SETTLE_PERIOD` to 30 minutes so engagement
lands before scoring, and updates the built-in crawler data explorer
reports to bucket on the 60-point threshold and to count only beacon
pageviews.
2026-07-29 13:13:42 +08:00

283 lines
10 KiB
Ruby
Vendored

# frozen_string_literal: true
RSpec.describe BrowserPageviewSessionEngagementDailyRollup do
describe ".aggregate" do
let(:start_date) { Date.new(2026, 6, 1) }
let(:end_date) { Date.new(2026, 6, 30) }
before { freeze_time(Time.utc(2026, 6, 20, 12, 0, 0)) }
it "bounces a single-pageview session with fewer than 10 engaged seconds" do
event = Fabricate(:browser_pageview_event, created_at: Time.utc(2026, 6, 10, 9))
Fabricate(
:browser_pageview_session_engagement,
session_id: event.session_id,
engaged_seconds: 9,
)
described_class.aggregate(start_date:, end_date:)
expect(described_class.all).to contain_exactly(have_attributes(sessions: 1, bounced: 1))
end
it "bounces a single-pageview session that has no engagement row" do
Fabricate(:browser_pageview_event, created_at: Time.utc(2026, 6, 10, 9))
described_class.aggregate(start_date:, end_date:)
expect(described_class.all).to contain_exactly(
have_attributes(sessions: 1, bounced: 1, engaged_seconds_total: 0),
)
end
it "does not bounce a single-pageview session with 10 or more engaged seconds" do
event = Fabricate(:browser_pageview_event, created_at: Time.utc(2026, 6, 10, 9))
Fabricate(
:browser_pageview_session_engagement,
session_id: event.session_id,
engaged_seconds: 10,
)
described_class.aggregate(start_date:, end_date:)
expect(described_class.all).to contain_exactly(have_attributes(sessions: 1, bounced: 0))
end
it "does not bounce a multi-pageview session even with fewer than 10 engaged seconds" do
event = Fabricate(:browser_pageview_event, created_at: Time.utc(2026, 6, 10, 9, 0))
Fabricate(
:browser_pageview_event,
session_id: event.session_id,
created_at: Time.utc(2026, 6, 10, 9, 1),
)
Fabricate(
:browser_pageview_session_engagement,
session_id: event.session_id,
engaged_seconds: 2,
)
described_class.aggregate(start_date:, end_date:)
expect(described_class.all).to contain_exactly(
have_attributes(sessions: 1, bounced: 0, engaged_seconds_total: 2),
)
end
it "does not bounce a multi-pageview session that has no engagement row" do
event = Fabricate(:browser_pageview_event, created_at: Time.utc(2026, 6, 10, 9, 0))
Fabricate(
:browser_pageview_event,
session_id: event.session_id,
created_at: Time.utc(2026, 6, 10, 9, 1),
)
described_class.aggregate(start_date:, end_date:)
expect(described_class.all).to contain_exactly(have_attributes(sessions: 1, bounced: 0))
end
it "does not bounce a session whose second pageview falls past the range end" do
freeze_time(Time.utc(2026, 7, 15, 12, 0, 0))
event = Fabricate(:browser_pageview_event, created_at: Time.utc(2026, 6, 30, 23, 30))
Fabricate(
:browser_pageview_event,
session_id: event.session_id,
created_at: Time.utc(2026, 7, 1, 0, 30),
)
described_class.aggregate(start_date:, end_date:)
expect(described_class.all).to contain_exactly(have_attributes(sessions: 1, bounced: 0))
end
it "splits sessions and bounced counts by the session's logged-in state" do
user = Fabricate(:user)
Fabricate(:browser_pageview_event, user_id: user.id, created_at: Time.utc(2026, 6, 10, 9))
Fabricate(:browser_pageview_event, created_at: Time.utc(2026, 6, 10, 9))
described_class.aggregate(start_date:, end_date:)
rows =
described_class.order(:logged_in).pluck(
:logged_in,
:sessions,
:bounced,
:engaged_seconds_total,
)
expect(rows).to eq([[false, 1, 1, 0], [true, 1, 1, 0]])
end
it "attributes a session spanning UTC midnight to its first pageview's UTC date" do
event = Fabricate(:browser_pageview_event, created_at: Time.utc(2026, 6, 10, 23, 30))
Fabricate(
:browser_pageview_event,
session_id: event.session_id,
created_at: Time.utc(2026, 6, 11, 0, 30),
)
Fabricate(
:browser_pageview_session_engagement,
session_id: event.session_id,
engaged_seconds: 42,
)
described_class.aggregate(start_date:, end_date:)
expect(described_class.pluck(:date)).to eq([Date.new(2026, 6, 10)])
end
it "sums engaged seconds per date and logged-in state" do
user = Fabricate(:user)
anon_event = Fabricate(:browser_pageview_event, created_at: Time.utc(2026, 6, 10, 9))
logged_in_event =
Fabricate(:browser_pageview_event, user_id: user.id, created_at: Time.utc(2026, 6, 10, 9))
Fabricate(
:browser_pageview_session_engagement,
session_id: anon_event.session_id,
engaged_seconds: 30,
)
Fabricate(
:browser_pageview_session_engagement,
session_id: logged_in_event.session_id,
engaged_seconds: 70,
)
described_class.aggregate(start_date:, end_date:)
totals = described_class.order(:logged_in).pluck(:logged_in, :engaged_seconds_total)
expect(totals).to eq([[false, 30], [true, 70]])
end
it "excludes engagement rows whose session has no pageview events" do
Fabricate(:browser_pageview_session_engagement, engaged_seconds: 120)
described_class.aggregate(start_date:, end_date:)
expect(described_class.count).to eq(0)
end
it "counts only rollup-source pageviews, even within a single session" do
SiteSetting.dashboard_improvements = true
UpcomingChangeEvent.create!(
upcoming_change_name: "dashboard_improvements",
event_type: :manual_opt_in,
created_at: Time.utc(2026, 6, 1, 9),
)
beacon_event =
Fabricate(:browser_pageview_event, source: :beacon, created_at: Time.utc(2026, 6, 10, 9))
Fabricate(
:browser_pageview_event,
session_id: beacon_event.session_id,
source: :piggyback,
created_at: Time.utc(2026, 6, 10, 10),
)
Fabricate(:browser_pageview_event, source: :piggyback, created_at: Time.utc(2026, 6, 10, 9))
described_class.aggregate(start_date:, end_date:)
expect(described_class.all).to contain_exactly(have_attributes(sessions: 1, bounced: 1))
end
it "clears a session's previous logged-in partition when it flips across runs" do
event = Fabricate(:browser_pageview_event, created_at: Time.utc(2026, 6, 10, 9))
described_class.aggregate(start_date:, end_date:)
user = Fabricate(:user)
Fabricate(
:browser_pageview_event,
session_id: event.session_id,
user_id: user.id,
created_at: Time.utc(2026, 6, 10, 10),
)
described_class.aggregate(start_date:, end_date:)
expect(described_class.all).to contain_exactly(have_attributes(logged_in: true, sessions: 1))
end
it "keeps an existing rollup for a windowed date whose source events were pruned" do
Fabricate(
:browser_pageview_session_engagement_daily_rollup,
date: Date.new(2026, 6, 10),
sessions: 5,
)
Fabricate(:browser_pageview_event, created_at: Time.utc(2026, 6, 12, 9))
described_class.aggregate(start_date:, end_date:)
expect(described_class.where(date: Date.new(2026, 6, 10)).pick(:sessions)).to eq(5)
end
it "aggregates sessions on the range's boundary days but excludes those outside it" do
freeze_time(Time.utc(2026, 7, 15, 12, 0, 0))
Fabricate(:browser_pageview_event, created_at: Time.utc(2026, 6, 10, 9))
Fabricate(:browser_pageview_event, created_at: Time.utc(2026, 6, 1, 0, 0))
Fabricate(:browser_pageview_event, created_at: Time.utc(2026, 6, 30, 23, 0))
Fabricate(:browser_pageview_event, created_at: Time.utc(2026, 5, 10, 9))
Fabricate(:browser_pageview_event, created_at: Time.utc(2026, 7, 10, 9))
described_class.aggregate(start_date:, end_date:)
expect(described_class.pluck(:date)).to contain_exactly(
Date.new(2026, 6, 1),
Date.new(2026, 6, 10),
Date.new(2026, 6, 30),
)
end
it "excludes a session whose first pageview is before the range but continues inside it" do
event = Fabricate(:browser_pageview_event, created_at: Time.utc(2026, 5, 31, 23, 30))
Fabricate(
:browser_pageview_event,
session_id: event.session_id,
created_at: Time.utc(2026, 6, 1, 0, 30),
)
described_class.aggregate(start_date:, end_date:)
expect(described_class.count).to eq(0)
end
it "excludes sessions that started within the beacon settle period" do
Fabricate(:browser_pageview_event, created_at: Time.utc(2026, 6, 20, 11, 55))
Fabricate(:browser_pageview_event, created_at: Time.utc(2026, 6, 20, 10, 45))
described_class.aggregate(start_date:, end_date:)
expect(described_class.all).to contain_exactly(have_attributes(sessions: 1))
end
it "does not bounce a session whose second pageview arrived within the beacon settle period" do
event = Fabricate(:browser_pageview_event, created_at: Time.utc(2026, 6, 20, 10, 30))
Fabricate(
:browser_pageview_event,
session_id: event.session_id,
created_at: Time.utc(2026, 6, 20, 11, 58),
)
described_class.aggregate(start_date:, end_date:)
expect(described_class.all).to contain_exactly(have_attributes(sessions: 1, bounced: 0))
end
it "updates existing rows when re-aggregating with new sessions" do
first_event = Fabricate(:browser_pageview_event, created_at: Time.utc(2026, 6, 10, 9))
Fabricate(
:browser_pageview_session_engagement,
session_id: first_event.session_id,
engaged_seconds: 5,
)
described_class.aggregate(start_date:, end_date:)
second_event = Fabricate(:browser_pageview_event, created_at: Time.utc(2026, 6, 10, 9))
Fabricate(
:browser_pageview_session_engagement,
session_id: second_event.session_id,
engaged_seconds: 5,
)
described_class.aggregate(start_date:, end_date:)
expect(described_class.all).to contain_exactly(
have_attributes(sessions: 2, bounced: 2, engaged_seconds_total: 10),
)
end
end
end