Guarantee articles in main article feed (#6490) [deploy]

* Adjust feed offset, remove time limits for main feed

* Improve offset construction readability
This commit is contained in:
Josh Puetz 2020-03-06 12:29:07 -06:00 committed by GitHub
parent 36147cce28
commit e2f190512b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 64 additions and 18 deletions

View file

@ -1,5 +1,7 @@
module Articles
class Feed
RANDOM_OFFSET_VALUES = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 3, 3, 4, 5, 6, 7, 8, 9, 10, 11].freeze
def initialize(user: nil, number_of_articles: 35, page: 1, tag: nil)
@user = user
@number_of_articles = number_of_articles
@ -137,10 +139,10 @@ module Articles
order("hotness_score DESC")
featured_story = hot_stories.where.not(main_image: nil).first
if user_signed_in
offset = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 3, 3, 4, 5, 6, 7, 8, 9, 10, 11].sample # random offset, weighted more towards zero
offset = RANDOM_OFFSET_VALUES.select { |i| i < hot_stories.count }.sample # random offset, weighted more towards zero
hot_stories = hot_stories.offset(offset)
new_stories = Article.published.
where("published_at > ? AND score > ?", rand(2..6).hours.ago, -15).
where("score > ?", -15).
limited_column_select.order("published_at DESC").limit(rand(15..80))
hot_stories = hot_stories.to_a + new_stories.to_a
end

View file

@ -77,12 +77,10 @@ RSpec.describe Articles::Feed, type: :service do
let(:featured_story) { result.first }
let(:stories) { result.second }
it "only includes stories from less than 6 hours ago" do
expect(stories).not_to include(old_story)
expect(stories).not_to include(article)
# Ideally we'd test for hot_story in the stories list, but the random offset selection makes that random
expect(featured_story).to eq(hot_story)
it "only includes stories" do
expect(stories).to include(old_story)
expect(stories).to include(article)
expect(stories).to include(hot_story)
end
end
@ -132,8 +130,8 @@ RSpec.describe Articles::Feed, type: :service do
context "when user logged in" do
let(:stories) { feed.default_home_feed(user_signed_in: true) }
it "includes stories from between 2 and 6 hours ago" do
expect(stories).not_to include(old_story)
it "includes stories " do
expect(stories).to include(old_story)
expect(stories).to include(new_story)
end
end
@ -143,8 +141,8 @@ RSpec.describe Articles::Feed, type: :service do
let!(:new_story) { create(:article, published_at: 10.minutes.ago, score: 10) }
let(:stories) { feed.default_home_feed_with_more_randomness }
it "includes stories from between 2 and 6 hours ago" do
expect(stories).not_to include(old_story)
it "includes stories" do
expect(stories).to include(old_story)
expect(stories).to include(new_story)
end
end
@ -153,8 +151,8 @@ RSpec.describe Articles::Feed, type: :service do
let!(:new_story) { create(:article, published_at: 10.minutes.ago, score: 10) }
let(:stories) { feed.mix_default_and_more_random }
it "includes stories from between 2 and 6 hours ago" do
expect(stories).not_to include(old_story)
it "includes stories" do
expect(stories).to include(old_story)
expect(stories).to include(new_story)
end
end
@ -163,8 +161,8 @@ RSpec.describe Articles::Feed, type: :service do
let!(:new_story) { create(:article, published_at: 10.minutes.ago, score: 10) }
let(:stories) { feed.more_tag_weight }
it "includes stories from between 2 and 6 hours ago" do
expect(stories).not_to include(old_story)
it "includes stories" do
expect(stories).to include(old_story)
expect(stories).to include(new_story)
end
end
@ -173,8 +171,8 @@ RSpec.describe Articles::Feed, type: :service do
let!(:new_story) { create(:article, published_at: 10.minutes.ago, score: 10) }
let(:stories) { feed.more_tag_weight_more_random }
it "includes stories from between 2 and 6 hours ago" do
expect(stories).not_to include(old_story)
it "includes stories" do
expect(stories).to include(old_story)
expect(stories).to include(new_story)
end
end
@ -375,6 +373,52 @@ RSpec.describe Articles::Feed, type: :service do
end
end
describe ".globally_hot_articles" do
let!(:recently_published_article) { create(:article, published_at: 3.hours.ago) }
let(:globally_hot_articles) { feed.globally_hot_articles(true).second }
it "returns hot stories" do
expect(globally_hot_articles).not_to be_empty
end
it "returns recent stories" do
expect(globally_hot_articles).to include(recently_published_article)
end
context "when low number of hot stories and no recently published articles" do
before do
Article.delete_all
create(:article, hotness_score: 1000, score: 1000, published_at: 3.hours.ago)
end
# This test handles a situation in which there are a low number of hot or new stories, and the user is logged in.
# Previously the offest factor could result in zero stories being returned sometimes.
# We manually called `feed.globally_hot_articles` here because `let` caches it!
it "still returns articles" do
empty_feed = false
20.times do
if feed.globally_hot_articles(true).second.empty?
empty_feed = true
break
end
end
expect(empty_feed).to be false
end
end
context "when now hot stories and no recently published articles" do
before do
Article.delete_all
create(:article, hotness_score: 0, score: 0, published_at: 3.days.ago)
end
it "still returns articles" do
expect(globally_hot_articles).not_to be_empty
end
end
end
describe ".find_featured_story" do
let(:featured_story) { described_class.find_featured_story(stories) }