favgroups: prevent adding invalid posts to favgroups.

Prevent adding duplicate or nonexistent posts to favgroups. This was
possible by editing the post_ids field directly on the favgroup edit
page.
This commit is contained in:
evazion
2020-01-16 11:53:53 -06:00
parent ab325c5d2b
commit 9ec30c85cf
2 changed files with 54 additions and 51 deletions

View File

@@ -6,6 +6,7 @@ class FavoriteGroup < ApplicationRecord
before_validation :strip_name before_validation :strip_name
validate :creator_can_create_favorite_groups, :on => :create validate :creator_can_create_favorite_groups, :on => :create
validate :validate_number_of_posts validate :validate_number_of_posts
validate :validate_posts
array_attribute :post_ids, parse: /\d+/, cast: :to_i array_attribute :post_ids, parse: /\d+/, cast: :to_i
@@ -93,6 +94,21 @@ class FavoriteGroup < ApplicationRecord
end end
end end
def validate_posts
added_post_ids = post_ids - post_ids_was
existing_post_ids = Post.where(id: added_post_ids).pluck(:id)
nonexisting_post_ids = added_post_ids - existing_post_ids
if nonexisting_post_ids.present?
errors[:base] << "Cannot add invalid post(s) to favgroup: #{nonexisting_post_ids.to_sentence}"
end
duplicate_post_ids = post_ids.group_by(&:itself).transform_values(&:size).select { |id, count| count > 1 }.keys
if duplicate_post_ids.present?
errors[:base] << "Favgroup already contains post #{duplicate_post_ids.to_sentence}"
end
end
def self.normalize_name(name) def self.normalize_name(name)
name.gsub(/[[:space:]]+/, "_") name.gsub(/[[:space:]]+/, "_")
end end
@@ -126,14 +142,12 @@ class FavoriteGroup < ApplicationRecord
def add!(post) def add!(post)
with_lock do with_lock do
return if contains?(post.id)
update!(post_ids: post_ids + [post.id]) update!(post_ids: post_ids + [post.id])
end end
end end
def remove!(post) def remove!(post)
with_lock do with_lock do
return unless contains?(post.id)
update!(post_ids: post_ids - [post.id]) update!(post_ids: post_ids - [post.id])
end end
end end

View File

@@ -2,76 +2,65 @@ require 'test_helper'
class FavoriteTest < ActiveSupport::TestCase class FavoriteTest < ActiveSupport::TestCase
def setup def setup
super @user = create(:user)
@user = FactoryBot.create(:user)
CurrentUser.user = @user CurrentUser.user = @user
CurrentUser.ip_addr = "127.0.0.1" CurrentUser.ip_addr = "127.0.0.1"
@fav_group = FactoryBot.create(:favorite_group, creator: @user, name: "blah") @fav_group = create(:favorite_group, creator: @user, name: "blah")
end end
def teardown def teardown
super
CurrentUser.user = nil CurrentUser.user = nil
CurrentUser.ip_addr = nil CurrentUser.ip_addr = nil
end end
context "searching by post id" do context "searching by post id" do
context "when the id is the only one" do should "return the fav group" do
setup do posts = create_list(:post, 3)
@fav_group.post_ids = "1"
@fav_group.save
end
should "return the fav group" do @fav_group.add!(posts[0])
assert_equal(@fav_group.id, FavoriteGroup.for_post(1).first.try(:id)) assert_equal(@fav_group.id, FavoriteGroup.for_post(posts[0].id).first.id)
end
end
context "when the id is in the beginning" do @fav_group.add!(posts[1])
setup do assert_equal(@fav_group.id, FavoriteGroup.for_post(posts[1].id).first.id)
@fav_group.post_ids = "1 2"
@fav_group.save
end
should "return the fav group" do @fav_group.add!(posts[2])
assert_equal(@fav_group.id, FavoriteGroup.for_post(1).first.try(:id)) assert_equal(@fav_group.id, FavoriteGroup.for_post(posts[2].id).first.id)
end
end
context "when the id is in the middle" do
setup do
@fav_group.post_ids = "3 1 2"
@fav_group.save
end
should "return the fav group" do
assert_equal(@fav_group.id, FavoriteGroup.for_post(1).first.try(:id))
end
end
context "when the id is in the end" do
setup do
@fav_group.post_ids = "2 1"
@fav_group.save
end
should "return the fav group" do
assert_equal(@fav_group.id, FavoriteGroup.for_post(1).first.try(:id))
end
end end
end end
context "expunging a post" do context "expunging a post" do
setup do should "remove it from all favorite groups" do
@post = FactoryBot.create(:post) @post = FactoryBot.create(:post)
@fav_group.add!(@post) @fav_group.add!(@post)
assert_equal([@post.id], @fav_group.post_ids)
@post.expunge!
assert_equal([], @fav_group.reload.post_ids)
end
end
context "adding a post to a favgroup" do
should "not allow adding duplicate posts" do
post = create(:post)
@fav_group.add!(post)
assert(@fav_group.valid?)
assert_equal([post.id], @fav_group.reload.post_ids)
assert_raise(ActiveRecord::RecordInvalid) { @fav_group.add!(post) }
assert_equal([post.id], @fav_group.reload.post_ids)
@fav_group.reload.update(post_ids: [post.id, post.id])
refute(@fav_group.valid?)
assert_equal([post.id], @fav_group.reload.post_ids)
end end
should "remove it from all favorite groups" do should "not allow adding nonexistent posts" do
assert_equal([@post.id], @fav_group.post_ids) @fav_group.update(post_ids: [0])
@post.expunge!
@fav_group.reload refute(@fav_group.valid?)
assert_equal([], @fav_group.post_ids) assert_equal([], @fav_group.reload.post_ids)
end end
end end
end end