* Moving Experiments into AbExperiment namespace This commit entails two major concepts: 1. Extracting logic out of a worker. 2. Cleaning the conditions in which we convert experiments. In addition, I revisited some spec names to tidy them up for clarification and structure. **Extracting Logic...** For workers, my preference is that they be a simple router to another object. A non-worker object, with it's initialize method, can both better manage instance variables. Further, workers are somewhat "flat" in hierarchy, In this case, having the AbExperiment be knowledgeable both in setting the experiment and handling conversions makes (to me) organizational sense. For example, the AbExperiment is constructed as a wrapper to the FieldTest gem. But the RecordFieldTestEventWorker had knowledge of FieldTest. With this refactor, it does not have that knowledge. This also provided an opportunity to replace magic strings with constants. **Cleaning the conditions...** This relates directly to and closes forem/forem#17530. I was looking at the experiments after 2 days, and realized that there shouldn't be results for `user_views_pages_on_at_least_nine_different_days_within_two_weeks`; after all the experiment started 2 days ago, how can we have results that look at 2 weeks. By adding the "max" function calls, we ensure a clear boundary of "before the experiment began" versus "while the experiment is running". Closes forem/forem#17530 * Update spec/models/ab_experiment_spec.rb Co-authored-by: Mac Siri <krairit.siri@gmail.com> * Apply suggestions from code review Co-authored-by: Julianna Tetreault <32834804+juliannatetreault@users.noreply.github.com> Co-authored-by: Mac Siri <krairit.siri@gmail.com> Co-authored-by: Julianna Tetreault <32834804+juliannatetreault@users.noreply.github.com>
151 lines
5.9 KiB
Ruby
151 lines
5.9 KiB
Ruby
require "rails_helper"
|
|
RSpec.describe AbExperiment::GoalConversionHandler do
|
|
include FieldTest::Helpers
|
|
|
|
describe ".call" do
|
|
subject(:handler) { described_class.new(user: user, goal: goal, experiments: experiments) }
|
|
|
|
let(:user) { create(:user) }
|
|
let(:goal) { "non_sense" }
|
|
let(:experiments) { FieldTest.config["experiments"] }
|
|
|
|
context "with no experiments" do
|
|
let(:experiments) { nil }
|
|
|
|
it "gracefully handles a case where there are no tests" do
|
|
handler.call
|
|
expect(FieldTest::Event.all.size).to be(0)
|
|
end
|
|
end
|
|
|
|
context "with experiment that started to soon for some results" do
|
|
let(:goal) { described_class::USER_CREATES_COMMENT_GOAL }
|
|
let(:experiment_name) { AbExperiment::CURRENT_FEED_STRATEGY_EXPERIMENT }
|
|
# NOTE: We're choosing a future date as a logic short-cut for comment tests
|
|
let(:experiments) { { experiment_name => { "start_date" => 8.days.from_now } } }
|
|
|
|
before do
|
|
field_test(experiment_name, participant: user)
|
|
end
|
|
|
|
it "registers some events but not others", :aggregate_failures do
|
|
7.times do |n|
|
|
create(:comment, user_id: user.id, created_at: n.days.ago)
|
|
end
|
|
handler.call
|
|
# Only the comment registered; the other one would've registered had the events happened
|
|
# after the experiment start date.
|
|
expect(FieldTest::Event.where(name: goal).count).to eq(1)
|
|
expect(FieldTest::Event
|
|
.where(name: "user_creates_comment_on_at_least_four_different_days_within_a_week")
|
|
.count).to eq(0)
|
|
end
|
|
end
|
|
|
|
context "with user who is part of field test and user_creates_comment goal" do
|
|
let(:goal) { described_class::USER_CREATES_COMMENT_GOAL }
|
|
|
|
before do
|
|
field_test(AbExperiment::CURRENT_FEED_STRATEGY_EXPERIMENT, participant: user)
|
|
end
|
|
|
|
it "records a conversion", :aggregate_failures do
|
|
handler.call
|
|
expect(FieldTest::Event.last.field_test_membership.participant_id).to eq(user.id.to_s)
|
|
expect(FieldTest::Event.last.name).to eq(goal)
|
|
end
|
|
|
|
it "records user_creates_comment_on_at_least_four_different_days_within_a_week field test conversion", :aggregate_failures do
|
|
7.times do |n|
|
|
create(:comment, user_id: user.id, created_at: n.days.ago)
|
|
end
|
|
handler.call
|
|
expect(FieldTest::Event.last.field_test_membership.participant_id).to eq(user.id.to_s)
|
|
expect(FieldTest::Event.last.name)
|
|
.to eq("user_creates_comment_on_at_least_four_different_days_within_a_week")
|
|
end
|
|
end
|
|
|
|
context "with user who is part of field test and user_creates_pageview goal" do
|
|
let(:goal) { described_class::USER_CREATES_PAGEVIEW_GOAL }
|
|
|
|
before do
|
|
field_test(AbExperiment::CURRENT_FEED_STRATEGY_EXPERIMENT, participant: user)
|
|
end
|
|
|
|
it "records user_views_pages_on_at_least_four_different_days_within_a_week field test conversion", :aggregate_failures do
|
|
7.times do |n|
|
|
create(:page_view, user_id: user.id, created_at: n.days.ago)
|
|
end
|
|
handler.call
|
|
expect(FieldTest::Event.last.field_test_membership.participant_id).to eq(user.id.to_s)
|
|
expect(FieldTest::Event.pluck(:name))
|
|
.to include("user_views_pages_on_at_least_four_different_days_within_a_week")
|
|
end
|
|
|
|
it "records user_views_pages_on_at_least_nine_different_days_within_two_weeks field test conversionn", :aggregate_failures do
|
|
10.times do |n|
|
|
create(:page_view, user_id: user.id, created_at: n.days.ago)
|
|
end
|
|
handler.call
|
|
expect(FieldTest::Event.last.field_test_membership.participant_id).to eq(user.id.to_s)
|
|
expect(FieldTest::Event.pluck(:name))
|
|
.to include("user_views_pages_on_at_least_nine_different_days_within_two_weeks")
|
|
end
|
|
|
|
it "records user_views_pages_on_at_least_twelve_different_hours_within_five_days field test conversion", :aggregate_failures do
|
|
15.times do |n|
|
|
create(:page_view, user_id: user.id, created_at: n.hours.ago)
|
|
end
|
|
|
|
handler.call
|
|
expect(FieldTest::Event.last.field_test_membership.participant_id).to eq(user.id.to_s)
|
|
expect(FieldTest::Event.pluck(:name))
|
|
.to include("user_views_pages_on_at_least_twelve_different_hours_within_five_days")
|
|
end
|
|
|
|
it "does not record field test conversion if not qualifying" do
|
|
2.times do |n|
|
|
create(:page_view, user_id: user.id, created_at: n.days.ago)
|
|
end
|
|
handler.call
|
|
expect(FieldTest::Event.all.size).to be(0)
|
|
end
|
|
|
|
it "records user_views_pages_on_at_least_four_different_hours_within_a_day field test conversionn", :aggregate_failures do
|
|
7.times do |n|
|
|
create(:page_view, user_id: user.id, created_at: n.hours.ago)
|
|
end
|
|
handler.call
|
|
expect(FieldTest::Event.last.field_test_membership.participant_id).to eq(user.id.to_s)
|
|
expect(FieldTest::Event.pluck(:name))
|
|
.to include("user_views_pages_on_at_least_four_different_hours_within_a_day")
|
|
end
|
|
|
|
it "does not record user_views_article_four_hours_in_day field test conversion for non-qualifying activity" do
|
|
2.times do |n|
|
|
create(:page_view, user_id: user.id, created_at: n.hours.ago)
|
|
end
|
|
handler.call
|
|
expect(FieldTest::Event.all.size).to be(0)
|
|
end
|
|
end
|
|
|
|
context "with user who is not part of field test" do
|
|
let(:goal) { described_class::USER_CREATES_COMMENT_GOAL }
|
|
|
|
it "does not register a conversion" do
|
|
handler.call
|
|
expect(FieldTest::Event.all.size).to be(0)
|
|
end
|
|
|
|
it "records user_views_article_four_days_in_week field test conversion" do
|
|
7.times do |n|
|
|
create(:page_view, user_id: user.id, created_at: n.days.ago)
|
|
end
|
|
handler.call
|
|
expect(FieldTest::Event.all.size).to be(0)
|
|
end
|
|
end
|
|
end
|
|
end
|