Load article before creating a page view for an invalid one (#15898)

* Load article before creating a page view for an invalid one

This should resolve a validation error in PageView.create! when the
article does not exist (perhaps it was deleted, or the user suspended,
or it was invalid data sent from the client).

This had been happening dozens of times per day for the last 10
months.

* Use an intention revealing symbol instead of calculated id

Since we only require that find_by not find anything, pass a symbol
that's not going to be the id for any article under any conditions.

As an added benefit, this provides a clear indication of the purpose
of the symbol, without needing to mentally confirm that

```sql
  SELECT * FROM articles
  WHERE id IN (
    SELECT (1 + MAX(id)) FROM articles
  ) LIMIT 1
```

actually never gives any articles back.
This commit is contained in:
Daniel Uber 2021-12-29 09:48:34 -06:00 committed by GitHub
parent a3b7f3c90b
commit ba590c1750
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 38 additions and 1 deletions

View file

@ -8,9 +8,11 @@ module Articles
retry: false
def perform(create_params)
article = Article.find_by(id: create_params["article_id"])
return unless article
PageView.create!(create_params)
article = Article.find(create_params["article_id"])
updated_count = article.page_views.sum(:counts_for_number_of_views)
if updated_count > article.page_views_count
article.update_column(:page_views_count, updated_count)

View file

@ -0,0 +1,35 @@
require "rails_helper"
RSpec.describe Articles::UpdatePageViewsWorker, type: :worker do
let(:worker) { described_class.new }
context "when the article id is invalid" do
let(:article_id) { :no_article_with_this_id }
it "exits gracefully" do
expect { worker.perform(article_id: article_id) }.not_to raise_error
end
it "does not attempt to create a page view for an invalid article" do
allow(PageView).to receive(:create!)
worker.perform(article_id: article_id)
expect(PageView).not_to have_received(:create!)
end
end
context "when the article exists" do
let(:user) { create(:user) }
let(:referrer) { nil }
let(:article) { create(:article) }
it "creates a page view" do
expect do
worker.perform("article_id" => article.id,
"user_id" => user.id,
"referrer" => referrer)
end.to change(PageView, :count).by(1)
end
end
end