eBooru/test/unit/post_test.rb
Earlopain c53b2eff60
[Tests] More performance improvements
Most of the gained time is because some post tests don't recreate the
elastic index twice now.
Tests go from 107 seconds to 95 on my machine.
2023-09-08 19:51:35 +02:00

2240 lines
76 KiB
Ruby

require 'test_helper'
class PostTest < ActiveSupport::TestCase
def assert_tag_match(posts, query)
assert_equal(posts.map(&:id), Post.tag_match(query).records.pluck(:id))
end
setup do
@user = create(:user, created_at: 2.weeks.ago)
CurrentUser.user = @user
reset_post_index
end
context "Deletion:" do
context "Expunging a post" do
# That belonged in a museum!
setup do
@upload = UploadService.new(attributes_for(:jpg_upload)).start!
@post = @upload.post
FavoriteManager.add!(user: @post.uploader, post: @post)
end
should "delete the files" do
assert_nothing_raised { @post.file(:preview) }
assert_nothing_raised { @post.file(:original) }
@post.expunge!
assert_raise(StandardError) { @post.file(:preview) }
assert_raise(StandardError) { @post.file(:original) }
end
should "remove all favorites" do
@post.expunge!
assert_equal(0, Favorite.for_user(@post.uploader_id).where("post_id = ?", @post.id).count)
end
should "decrement the uploader's upload count" do
assert_difference("@post.uploader.reload.post_upload_count", -1) do
@post.expunge!
end
end
should_eventually "decrement the user's note update count" do
create(:note, post: @post)
assert_difference(["@post.uploader.reload.note_update_count"], -1) do
@post.expunge!
end
end
should_eventually "decrement the user's post update count" do
assert_difference(["@post.uploader.reload.post_update_count"], -1) do
@post.expunge!
end
end
should "decrement the user's favorite count" do
assert_difference(["@post.uploader.reload.favorite_count"], -1) do
@post.expunge!
end
end
should "remove the post from iqdb" do
@post.expects(:remove_iqdb_async).once
@post.expunge!
end
context "that is status locked" do
setup do
@post.update(is_status_locked: true)
end
should "not destroy the record" do
@post.expunge!
assert_equal(1, Post.where("id = ?", @post.id).count)
end
end
context "that belongs to a pool" do
setup do
@pool = create(:pool)
@pool.add!(@post)
@post.expunge!
@pool.reload
end
should "remove the post from all pools" do
assert_equal([], @pool.post_ids)
end
should "destroy the record" do
assert_equal([], @post.errors.full_messages)
assert_equal(0, Post.where("id = ?", @post.id).count)
end
end
end
context "Deleting a post" do
context "that is status locked" do
setup do
@post = create(:post, is_status_locked: true)
end
should "fail" do
@post.delete!("test")
assert_equal(["Is status locked ; cannot delete post"], @post.errors.full_messages)
assert_equal(1, Post.where("id = ?", @post.id).count)
end
end
context "that is pending" do
setup do
@post = create(:post, is_pending: true)
end
should "succeed" do
@post.delete!("test")
assert_equal(true, @post.is_deleted)
assert_equal(1, @post.flags.size)
assert_match(/test/, @post.flags.last.reason)
end
end
context "that is still in cooldown after being flagged" do
should "succeed" do
flag = create(:post_flag)
assert_equal([], flag.errors.full_messages)
flag.post.delete!("test deletion")
assert_equal(true, flag.post.is_deleted)
assert_equal(2, flag.post.flags.size)
end
end
should "toggle the is_deleted flag" do
post = create(:post)
assert_equal(false, post.is_deleted?)
post.delete!("test")
assert_equal(true, post.is_deleted?)
end
end
end
context "Parent:" do
setup do
@parent = create(:post, tag_string: "a b c d", source: "a\nb\nc")
@post = create(:post, parent_id: @parent.id, tag_string: "c d e f", source: "b\nc\nd")
end
should "Copy tags to parent" do
@post.copy_tags_to_parent
@post.parent.save
assert_equal(@parent.reload.tag_string, "a b c d e f")
end
should "Copy sources to parent" do
@post.copy_sources_to_parent
@post.parent.save
assert_equal(@parent.reload.source, "a\nb\nc\nd")
end
end
context "Parenting:" do
context "Assigning a parent to a post" do
should "update the has_children flag on the parent" do
p1 = create(:post)
assert(!p1.has_children?, "Parent should not have any children")
c1 = create(:post, parent_id: p1.id)
p1.reload
assert(p1.has_children?, "Parent not updated after child was added")
end
should "update the has_children flag on the old parent" do
p1 = create(:post)
p2 = create(:post)
c1 = create(:post, parent_id: p1.id)
c1.parent_id = p2.id
c1.save
p1.reload
p2.reload
assert(!p1.has_children?, "Old parent should not have a child")
assert(p2.has_children?, "New parent should have a child")
end
end
context "Expunging a post with" do
context "a parent" do
should "reset the has_children flag of the parent" do
p1 = create(:post)
c1 = create(:post, parent_id: p1.id)
c1.expunge!
p1.reload
assert_equal(false, p1.has_children?)
end
should "update the parent's has_children flag" do
p1 = create(:post)
c1 = create(:post, parent_id: p1.id)
c1.expunge!
p1.reload
assert(!p1.has_children?, "Parent should not have children")
end
end
context "one child" do
should "remove the parent of that child" do
p1 = create(:post)
c1 = create(:post, parent_id: p1.id)
p1.expunge!
c1.reload
assert_nil(c1.parent)
end
end
context "two or more children" do
setup do
# ensure initial post versions won't be merged.
travel_to(1.day.ago) do
@p1 = create(:post)
@c1 = create(:post, parent_id: @p1.id)
@c2 = create(:post, parent_id: @p1.id)
@c3 = create(:post, parent_id: @p1.id)
end
end
should "reparent all children to the first child" do
@p1.expunge!
@c1.reload
@c2.reload
@c3.reload
assert_nil(@c1.parent_id)
assert_equal(@c1.id, @c2.parent_id)
assert_equal(@c1.id, @c3.parent_id)
end
should "save a post version record for each child" do
assert_difference(["@c1.versions.count", "@c2.versions.count", "@c3.versions.count"]) do
@p1.expunge!
@c1.reload
@c2.reload
@c3.reload
end
end
should "set the has_children flag on the new parent" do
@p1.expunge!
assert_equal(true, @c1.reload.has_children?)
end
end
end
context "Deleting a post with" do
context "a parent" do
should "not reassign favorites to the parent by default" do
p1 = create(:post)
c1 = create(:post, parent_id: p1.id)
user = create(:privileged_user)
FavoriteManager.add!(user: user, post: c1)
c1.delete!("test")
p1.reload
assert(Favorite.exists?(:post_id => c1.id, :user_id => user.id))
assert(!Favorite.exists?(:post_id => p1.id, :user_id => user.id))
end
should "reassign favorites to the parent if specified" do
p1 = create(:post)
c1 = create(:post, parent_id: p1.id)
user = create(:privileged_user)
FavoriteManager.add!(user: user, post: c1)
with_inline_jobs { c1.delete!("test", move_favorites: true) }
p1.reload
assert(!Favorite.exists?(:post_id => c1.id, :user_id => user.id), "Child should not still have favorites")
assert(Favorite.exists?(:post_id => p1.id, :user_id => user.id), "Parent should have favorites")
end
should "not update the parent's has_children flag" do
p1 = create(:post)
c1 = create(:post, parent_id: p1.id)
c1.delete!("test")
p1.reload
assert(p1.has_children?, "Parent should have children")
end
should "clear the has_active_children flag when the 'move favorites' option is set" do
user = create(:privileged_user)
p1 = create(:post)
c1 = create(:post, parent_id: p1.id)
FavoriteManager.add!(user: user, post: c1)
assert_equal(true, p1.reload.has_active_children?)
c1.delete!("test", :move_favorites => true)
assert_equal(false, p1.reload.has_active_children?)
end
end
context "one child" do
should "not remove the has_children flag" do
p1 = create(:post)
c1 = create(:post, parent_id: p1.id)
p1.delete!("test")
p1.reload
assert_equal(true, p1.has_children?)
end
should "not remove the parent of that child" do
p1 = create(:post)
c1 = create(:post, parent_id: p1.id)
p1.delete!("test")
c1.reload
assert_not_nil(c1.parent)
end
end
context "two or more children" do
should "not reparent all children to the first child" do
p1 = create(:post)
c1 = create(:post, parent_id: p1.id)
c2 = create(:post, parent_id: p1.id)
c3 = create(:post, parent_id: p1.id)
p1.delete!("test")
c1.reload
c2.reload
c3.reload
assert_equal(p1.id, c1.parent_id)
assert_equal(p1.id, c2.parent_id)
assert_equal(p1.id, c3.parent_id)
end
end
end
context "Undeleting a post with a parent" do
should "update with a new approver" do
new_user = create(:moderator_user)
p1 = create(:post)
c1 = create(:post, parent_id: p1.id)
c1.delete!("test")
as(new_user) do
c1.undelete!
end
p1.reload
assert_equal(new_user.id, c1.approver_id)
end
should "preserve the parent's has_children flag" do
p1 = create(:post)
c1 = create(:post, parent_id: p1.id)
c1.delete!("test")
c1.undelete!
p1.reload
assert_not_nil(c1.parent_id)
assert(p1.has_children?, "Parent should have children")
end
end
end
context "Moderation:" do
context "A deleted post" do
setup do
@post = create(:post, is_deleted: true)
end
context "that is status locked" do
setup do
@post.update(is_status_locked: true)
end
should "not allow undeletion" do
@post.undelete!
assert_equal(["Is status locked ; cannot undelete post"], @post.errors.full_messages)
assert_equal(true, @post.is_deleted?)
end
end
context "when undeleted" do
should "be undeleted" do
@post.undelete!
assert_equal(false, @post.reload.is_deleted?)
end
end
end
context "An approved post" do
should "be flagged" do
post = create(:post)
assert_difference(-> { PostFlag.count }, 1) do
create(:post_flag, post: post)
end
assert(post.is_flagged?, "Post should be flagged.")
assert_equal(1, post.flags.count)
end
should "not be flagged if no reason is given" do
post = create(:post)
assert_no_difference(-> { PostFlag.count }) do
post.flags.create(reason_name: "")
end
end
end
context "An unapproved post" do
should "preserve the approver's identity when approved" do
post = create(:post, is_pending: true)
post.approve!
assert_equal(post.approver_id, CurrentUser.id)
end
context "that was previously approved by person X" do
setup do
@user = create(:moderator_user, name: "xxx")
@user2 = create(:moderator_user, name: "yyy")
@post = create(:post)
@post.approve!(@user)
@post.unapprove!
end
should "allow person Y to approve the post" do
@post.approve!(@user2)
assert(@post.valid?)
end
end
context "that has been reapproved" do
setup do
@post = create(:post)
create(:post_flag, post: @post)
@post.reload
end
should "no longer be pending" do
@post.approve!
assert(@post.errors.empty?, @post.errors.full_messages.join(", "))
@post.reload
assert_equal(true, @post.is_flagged?)
assert_equal(false, @post.is_pending?)
end
end
end
context "A status locked post" do
setup do
@post = create(:post, is_status_locked: true, is_pending: true)
end
should "not allow new flags" do
flag = build(:post_flag, post: @post)
flag.validate
assert_equal(["Post is locked and cannot be flagged"], flag.errors.full_messages)
end
should "not allow approval" do
assert_no_difference(-> { PostApproval.count }) do
@post.approve!
end
end
end
end
context "Tagging:" do
context "A post" do
setup do
@post = create(:post)
end
context "as a new user" do
setup do
@post.update(:tag_string => "aaa bbb ccc ddd tagme")
CurrentUser.user = create(:user)
end
# TODO: This was moved to be a controller concern to fix issues with internal post updates
# should "not allow you to remove tags" do
# @post.update(:tag_string => "aaa")
# assert_equal(["You must have an account at least 1 week old to remove tags"], @post.errors.full_messages)
# end
should "allow you to remove request tags" do
@post.update(:tag_string => "aaa bbb ccc ddd")
@post.reload
assert_equal("aaa bbb ccc ddd", @post.tag_string)
end
end
context "with an artist tag that is then changed to copyright" do
setup do
CurrentUser.user = create(:janitor_user)
with_inline_jobs do
@post.update(tag_string: "art:abc")
@post.update(tag_string: "copy:abc")
end
@post.reload
end
should "update the category of the tag" do
assert_equal(Tag.categories.copyright, Tag.find_by_name("abc").category)
end
should "1234 update the category cache of the tag" do
assert_equal(Tag.categories.copyright, Cache.fetch("tc:abc"))
end
should "update the tag counts of the posts" do
assert_equal(0, @post.tag_count_artist)
assert_equal(1, @post.tag_count_copyright)
assert_equal(0, @post.tag_count_general)
end
end
context "using a tag prefix on an aliased tag" do
setup do
create(:tag_alias, antecedent_name: "abc", consequent_name: "xyz")
@post = Post.find(@post.id)
@post.update(:tag_string => "art:abc")
@post.reload
end
should "convert the tag to its normalized version" do
assert_equal("xyz", @post.tag_string)
end
end
context "with locked tags" do
context "without aliases or implications" do
setup do
@post = create(:post, locked_tags: "abc -what bcd -def", tag_string: "test_tag def")
end
should "contain correct tags" do
assert_equal("abc bcd test_tag", @post.tag_string)
end
end
context "with aliases" do
setup do
create(:tag_alias, antecedent_name: "abc", consequent_name: "xyz")
create(:tag_alias, antecedent_name: "def", consequent_name: "what")
@post = create(:post, locked_tags: "abc bcd -def", tag_string: "test_tag def what")
end
should "contain correct tags" do
assert_equal("bcd test_tag xyz", @post.tag_string)
end
end
context "with implications" do
should "contain correct tags" do
create(:tag_implication, antecedent_name: "specific_tag", consequent_name: "base_tag")
create(:tag_implication, antecedent_name: "female/female", consequent_name: "female")
create(:tag_implication, antecedent_name: "male/male", consequent_name: "male")
create(:tag_implication, antecedent_name: "trio", consequent_name: "group")
locked_tags = "-base_tag -female/female male/male test_tag"
tag_string = "specific_tag female/female group def"
@post = create(:post, locked_tags: locked_tags, tag_string: tag_string)
assert_equal("def group male male/male test_tag", @post.tag_string)
end
should "update the cache when implications change" do
@post = create(:post, locked_tags: "-group", tag_string: "trio specific_tag")
assert_equal("specific_tag trio", @post.tag_string)
ti = create(:tag_implication, antecedent_name: "trio", consequent_name: "group", status: "pending")
ti.approve!
create(:tag_implication, antecedent_name: "specific_tag", consequent_name: "base_tag", status: "pending")
@post.tag_string += " "
@post.save
assert_equal("specific_tag", @post.tag_string)
ti.reject!
@post.tag_string += " trio"
@post.save
assert_equal("specific_tag trio", @post.tag_string)
end
end
context "with dnp tags" do
should "prevent manually adding them" do
@post = create(:post, locked_tags: "", tag_string: "a b c")
@post.tag_string += " conditional_dnp"
@post.save
assert_equal("a b c", @post.tag_string)
assert_nil(@post.locked_tags)
end
should "add dnp tags through an implication" do
create(:tag_implication, antecedent_name: "artist", consequent_name: "avoid_posting")
@post = create(:post, locked_tags: "", tag_string: "a b c")
@post.tag_string += " artist"
@post.save
assert_equal("a artist avoid_posting b c", @post.tag_string)
assert_equal("avoid_posting", @post.locked_tags)
end
should "prevent removing them" do
create(:tag_implication, antecedent_name: "artist", consequent_name: "avoid_posting")
@post = create(:post, tag_string: "a artist avoid_posting b c")
@post.tag_string = "a b c"
@post.warnings.clear
@post.save
assert_match(/Forcefully added 1 locked tag: avoid_posting/, @post.warnings.full_messages.join(" "))
assert_equal("a avoid_posting b c", @post.tag_string)
end
should "not warn about dnp tags when dnp tags didn't change" do
create(:tag_implication, antecedent_name: "artist", consequent_name: "avoid_posting")
@post = create(:post, tag_string: "a artist avoid_posting b c")
@post.tag_string += "d e f"
@post.warnings.clear
@post.save
assert_no_match(/Forcefully added 1 locked tag: avoid_posting/, @post.warnings.full_messages.join(" "))
end
end
end
# TODO: Invalid tags are now reported as warnings, and don't trigger these.
# context "tagged with a valid tag" do
# subject { @post }
#
# should allow_value("touhou 100%").for(:tag_string)
# should allow_value("touhou FOO").for(:tag_string)
# should allow_value("touhou -foo").for(:tag_string)
# should allow_value("touhou pool:foo").for(:tag_string)
# should allow_value("touhou -pool:foo").for(:tag_string)
# should allow_value("touhou newpool:foo").for(:tag_string)
# should allow_value("touhou fav:self").for(:tag_string)
# should allow_value("touhou -fav:self").for(:tag_string)
# should allow_value("touhou upvote:self").for(:tag_string)
# should allow_value("touhou downvote:self").for(:tag_string)
# should allow_value("touhou parent:1").for(:tag_string)
# should allow_value("touhou child:1").for(:tag_string)
# should allow_value("touhou source:foo").for(:tag_string)
# should allow_value("touhou rating:z").for(:tag_string)
# should allow_value("touhou locked:rating").for(:tag_string)
# should allow_value("touhou -locked:rating").for(:tag_string)
#
# # \u3000 = ideographic space, \u00A0 = no-break space
# should allow_value("touhou\u3000foo").for(:tag_string)
# should allow_value("touhou\u00A0foo").for(:tag_string)
# end
# TODO: These are now warnings and don't trigger these.
# context "tagged with an invalid tag" do
# subject { @post }
#
# context "that doesn't already exist" do
# should_not allow_value("touhou user:evazion").for(:tag_string)
# should_not allow_value("touhou *~foo").for(:tag_string)
# should_not allow_value("touhou *-foo").for(:tag_string)
# should_not allow_value("touhou ,-foo").for(:tag_string)
#
# should_not allow_value("touhou ___").for(:tag_string)
# should_not allow_value("touhou ~foo").for(:tag_string)
# should_not allow_value("touhou _foo").for(:tag_string)
# should_not allow_value("touhou foo_").for(:tag_string)
# should_not allow_value("touhou foo__bar").for(:tag_string)
# should_not allow_value("touhou foo*bar").for(:tag_string)
# should_not allow_value("touhou foo,bar").for(:tag_string)
# should_not allow_value("touhou foo\abar").for(:tag_string)
# should_not allow_value("touhou café").for(:tag_string)
# should_not allow_value("touhou 東方").for(:tag_string)
# end
#
# context "that already exists" do
# setup do
# %W(___ ~foo _foo foo_ foo__bar foo*bar foo,bar foo\abar café 東方).each do |tag|
# build(:tag, name: tag).save(validate: false)
# end
# end
#
# should allow_value("touhou ___").for(:tag_string)
# should allow_value("touhou ~foo").for(:tag_string)
# should allow_value("touhou _foo").for(:tag_string)
# should allow_value("touhou foo_").for(:tag_string)
# should allow_value("touhou foo__bar").for(:tag_string)
# should allow_value("touhou foo*bar").for(:tag_string)
# should allow_value("touhou foo,bar").for(:tag_string)
# should allow_value("touhou foo\abar").for(:tag_string)
# should allow_value("touhou café").for(:tag_string)
# should allow_value("touhou 東方").for(:tag_string)
# end
# end
context "tagged with a metatag" do
context "for typing a tag" do
setup do
@post = create(:post, tag_string: "char:hoge")
@tags = @post.tag_array
end
should "change the type" do
assert(Tag.where(name: "hoge", category: 4).exists?, "expected 'moge' tag to be created as a character")
end
end
context "for a parent" do
setup do
@parent = create(:post)
end
should "update the parent relationships for both posts" do
@post.update(:tag_string => "aaa parent:#{@parent.id}")
@post.reload
@parent.reload
assert_equal(@parent.id, @post.parent_id)
assert(@parent.has_children?)
end
should "not allow self-parenting" do
@post.update(:tag_string => "parent:#{@post.id}")
assert_nil(@post.parent_id)
end
should "clear the parent with parent:none" do
@post.update(:parent_id => @parent.id)
assert_equal(@parent.id, @post.parent_id)
@post.update(:tag_string => "parent:none")
assert_nil(@post.parent_id)
end
should "clear the parent with -parent:1234" do
@post.update(:parent_id => @parent.id)
assert_equal(@parent.id, @post.parent_id)
@post.update(:tag_string => "-parent:#{@parent.id}")
assert_nil(@post.parent_id)
end
end
context "for a pool" do
context "on creation" do
setup do
@pool = create(:pool)
@post = create(:post, tag_string: "aaa pool:#{@pool.id}")
end
should "add the post to the pool" do
@post.reload
@pool.reload
assert_equal([@post.id], @pool.post_ids)
assert_equal("pool:#{@pool.id}", @post.pool_string)
end
end
context "negated" do
setup do
@pool = create(:pool)
@post = create(:post, tag_string: "aaa")
@pool.add(@post)
@post.tag_string = "aaa -pool:#{@pool.id}"
@post.save
end
should "remove the post from the pool" do
@post.reload
@pool.reload
assert_equal([], @pool.post_ids)
assert_equal("", @post.pool_string)
end
end
context "id" do
setup do
@pool = create(:pool)
@post.update(:tag_string => "aaa pool:#{@pool.id}")
end
should "add the post to the pool" do
@post.reload
@pool.reload
assert_equal([@post.id], @pool.post_ids)
assert_equal("pool:#{@pool.id}", @post.pool_string)
end
end
context "name" do
context "that exists" do
setup do
@pool = create(:pool, name: "abc")
@post.update(:tag_string => "aaa pool:abc")
end
should "add the post to the pool" do
@post.reload
@pool.reload
assert_equal([@post.id], @pool.post_ids)
assert_equal("pool:#{@pool.id}", @post.pool_string)
end
end
context "that doesn't exist" do
should "create a new pool and add the post to that pool" do
@post.update(:tag_string => "aaa newpool:abc")
@pool = Pool.find_by_name("abc")
@post.reload
assert_not_nil(@pool)
assert_equal([@post.id], @pool.post_ids)
assert_equal("pool:#{@pool.id}", @post.pool_string)
end
end
context "with special characters" do
should "not strip '%' from the name" do
@post.update(tag_string: "aaa newpool:ichigo_100%")
assert(Pool.exists?(name: "ichigo_100%"))
end
end
end
end
context "for a rating" do
context "that is valid" do
should "update the rating if the post is unlocked" do
@post.update(:tag_string => "aaa rating:e")
@post.reload
assert_equal("e", @post.rating)
end
end
context "that is invalid" do
should "not update the rating" do
@post.update(:tag_string => "aaa rating:z")
@post.reload
assert_equal("q", @post.rating)
end
end
context "that is locked" do
should "change the rating if locked in the same update" do
@post.update(tag_string: "rating:e", is_rating_locked: true)
assert(@post.valid?)
assert_equal("e", @post.reload.rating)
end
should "not change the rating if locked previously" do
@post.is_rating_locked = true
@post.save
@post.update(:tag_string => "rating:e")
assert(@post.invalid?)
assert_not_equal("e", @post.reload.rating)
end
end
end
context "for a fav" do
should "add/remove the current user to the post's favorite listing" do
@post.update(:tag_string => "aaa")
FavoriteManager.add!(user: CurrentUser.user, post: @post)
assert_equal("fav:#{@user.id}", @post.fav_string)
FavoriteManager.remove!(user: CurrentUser.user, post: @post)
assert_equal("", @post.fav_string)
end
end
context "for a child" do
should "add and remove children" do
@children = create_list(:post, 3, parent_id: nil)
@post.update(tag_string: "aaa child:#{@children.first.id}..#{@children.last.id}")
assert_equal(true, @post.reload.has_children?)
assert_equal(@post.id, @children[0].reload.parent_id)
assert_equal(@post.id, @children[1].reload.parent_id)
assert_equal(@post.id, @children[2].reload.parent_id)
@post.update(tag_string: "aaa -child:#{@children.first.id}")
assert_equal(true, @post.reload.has_children?)
assert_nil(@children[0].reload.parent_id)
assert_equal(@post.id, @children[1].reload.parent_id)
assert_equal(@post.id, @children[2].reload.parent_id)
@post.update(tag_string: "aaa child:none")
assert_equal(false, @post.reload.has_children?)
assert_nil(@children[0].reload.parent_id)
assert_nil(@children[1].reload.parent_id)
assert_nil(@children[2].reload.parent_id)
end
end
context "for a source" do
should "set the source with source:foo_bar_baz" do
@post.update(:tag_string => "source:foo_bar_baz")
assert_equal("foo_bar_baz", @post.source)
end
should 'set the source with source:"foo bar baz"' do
@post.update(:tag_string => 'source:"foo bar baz"')
assert_equal("foo bar baz", @post.source)
end
should 'strip the source with source:" foo bar baz "' do
@post.update(:tag_string => 'source:" foo bar baz "')
assert_equal("foo bar baz", @post.source)
end
should "clear the source with source:none" do
@post.update(:source => "foobar")
@post.update(:tag_string => "source:none")
assert_equal("", @post.source)
end
end
context "of" do
setup do
@janitor = create(:janitor_user)
end
context "locked:notes" do
context "by a member" do
should "not lock the notes" do
@post.update(:tag_string => "locked:notes")
assert_equal(false, @post.is_note_locked)
end
end
context "by a janitor" do
should "lock/unlock the notes" do
as(@janitor) do
@post.update(:tag_string => "locked:notes")
assert_equal(true, @post.is_note_locked)
@post.update(:tag_string => "-locked:notes")
assert_equal(false, @post.is_note_locked)
end
end
end
end
context "locked:rating" do
context "by a member" do
should "not lock the rating" do
@post.update(:tag_string => "locked:rating")
assert_equal(false, @post.is_rating_locked)
end
end
context "by a janitor" do
should "lock/unlock the rating" do
as(@janitor) do
@post.update(:tag_string => "locked:rating")
assert_equal(true, @post.is_rating_locked)
@post.update(:tag_string => "-locked:rating")
assert_equal(false, @post.is_rating_locked)
end
end
end
end
context "locked:status" do
context "by a member" do
should "not lock the status" do
@post.update(:tag_string => "locked:status")
assert_equal(false, @post.is_status_locked)
end
end
context "by an admin" do
should "lock/unlock the status" do
as(create(:admin_user)) do
@post.update(:tag_string => "locked:status")
assert_equal(true, @post.is_status_locked)
@post.update(:tag_string => "-locked:status")
assert_equal(false, @post.is_status_locked)
end
end
end
end
end
end
context "tagged with a negated tag" do
should "remove the tag if present" do
@post.update(:tag_string => "aaa bbb ccc")
@post.update(:tag_string => "aaa bbb ccc -bbb")
@post.reload
assert_equal("aaa ccc", @post.tag_string)
end
should "resolve aliases" do
create(:tag_alias, antecedent_name: "tr", consequent_name: "translation_request")
@post.update(:tag_string => "aaa translation_request -tr")
assert_equal("aaa", @post.tag_string)
end
end
context "tagged with animated_gif or animated_png" do
should "remove the tag if not a gif or png" do
@post.update(tag_string: "tagme animated_gif")
assert_equal("tagme", @post.tag_string)
@post.update(tag_string: "tagme animated_png")
assert_equal("tagme", @post.tag_string)
end
end
should "have an array representation of its tags" do
post = create(:post)
post.reload
post.set_tag_string("aaa bbb")
assert_equal(%w(aaa bbb), post.tag_array)
assert_equal(%w(tag1 tag2), post.tag_array_was)
end
context "with large dimensions" do
setup do
@post.image_width = 10_000
@post.image_height = 10
@post.tag_string = ""
@post.save
end
should "have the appropriate dimension tags added automatically" do
assert_match(/absurd_res/, @post.tag_string)
assert_match(/hi_res/, @post.tag_string)
end
end
context "with a large file size" do
setup do
@post.file_size = 31.megabytes
@post.tag_string = ""
@post.save
end
should "have the appropriate file size tags added automatically" do
assert_match(/huge_filesize/, @post.tag_string)
end
end
context "with a .webm file extension" do
setup do
create(:tag_implication, antecedent_name: "webm", consequent_name: "animated")
@post.file_ext = "webm"
@post.tag_string = ""
@post.save
end
should "have the appropriate file type tag added automatically" do
assert_match(/webm/, @post.tag_string)
end
should "apply implications after adding the file type tag" do
assert(@post.has_tag?("animated"), "expected 'webm' to imply 'animated'")
end
end
context "with a .swf file extension" do
setup do
@post.file_ext = "swf"
@post.tag_string = ""
@post.save
end
should "have the appropriate file type tag added automatically" do
assert_match(/flash/, @post.tag_string)
end
end
context "that has been updated" do
should "create a new version if it's the first version" do
assert_difference("PostVersion.count", 1) do
post = create(:post)
end
end
should "create a new version if the post is updated" do
post = create(:post)
assert_difference("PostVersion.count", 1) do
post.update(:tag_string => "zzz")
end
end
should "increment the updater's post_update_count" do
post = create(:post, tag_string: "aaa bbb ccc")
assert_difference("CurrentUser.user.reload.post_update_count", 1) do
post.update(:tag_string => "zzz")
end
end
should "reset its tag array cache" do
post = create(:post, tag_string: "aaa bbb ccc")
user = create(:user)
assert_equal(%w(aaa bbb ccc), post.tag_array)
post.tag_string = "ddd eee fff"
post.tag_string = "ddd eee fff"
post.save
assert_equal("ddd eee fff", post.tag_string)
assert_equal(%w(ddd eee fff), post.tag_array)
end
should "create the actual tag records" do
assert_difference("Tag.count", 3) do
post = create(:post, tag_string: "aaa bbb ccc")
end
end
should "update the post counts of relevant tag records" do
post1 = create(:post, tag_string: "aaa bbb ccc")
post2 = create(:post, tag_string: "bbb ccc ddd")
post3 = create(:post, tag_string: "ccc ddd eee")
assert_equal(1, Tag.find_by_name("aaa").post_count)
assert_equal(2, Tag.find_by_name("bbb").post_count)
assert_equal(3, Tag.find_by_name("ccc").post_count)
post3.reload
post3.tag_string = "xxx"
post3.save
assert_equal(1, Tag.find_by_name("aaa").post_count)
assert_equal(2, Tag.find_by_name("bbb").post_count)
assert_equal(2, Tag.find_by_name("ccc").post_count)
assert_equal(1, Tag.find_by_name("ddd").post_count)
assert_equal(0, Tag.find_by_name("eee").post_count)
assert_equal(1, Tag.find_by_name("xxx").post_count)
end
should "update its tag counts" do
artist_tag = create(:artist_tag)
copyright_tag = create(:copyright_tag)
general_tag = create(:tag)
new_post = create(:post, tag_string: "#{artist_tag.name} #{copyright_tag.name} #{general_tag.name}")
assert_equal(1, new_post.tag_count_artist)
assert_equal(1, new_post.tag_count_copyright)
assert_equal(1, new_post.tag_count_general)
assert_equal(0, new_post.tag_count_character)
assert_equal(3, new_post.tag_count)
new_post.tag_string = "babs"
new_post.save
assert_equal(0, new_post.tag_count_artist)
assert_equal(0, new_post.tag_count_copyright)
assert_equal(1, new_post.tag_count_general)
assert_equal(0, new_post.tag_count_character)
assert_equal(1, new_post.tag_count)
end
should "merge any tag changes that were made after loading the initial set of tags part 1" do
post = create(:post, tag_string: "aaa bbb ccc")
# user a adds <ddd>
post_edited_by_user_a = Post.find(post.id)
post_edited_by_user_a.old_tag_string = "aaa bbb ccc"
post_edited_by_user_a.tag_string = "aaa bbb ccc ddd"
post_edited_by_user_a.save
# user b removes <ccc> adds <eee>
post_edited_by_user_b = Post.find(post.id)
post_edited_by_user_b.old_tag_string = "aaa bbb ccc"
post_edited_by_user_b.tag_string = "aaa bbb eee"
post_edited_by_user_b.save
# final should be <aaa>, <bbb>, <ddd>, <eee>
final_post = Post.find(post.id)
assert_equal(%w(aaa bbb ddd eee), TagQuery.scan(final_post.tag_string).sort)
end
should "merge any tag changes that were made after loading the initial set of tags part 2" do
# This is the same as part 1, only the order of operations is reversed.
# The results should be the same.
post = create(:post, tag_string: "aaa bbb ccc")
# user a removes <ccc> adds <eee>
post_edited_by_user_a = Post.find(post.id)
post_edited_by_user_a.old_tag_string = "aaa bbb ccc"
post_edited_by_user_a.tag_string = "aaa bbb eee"
post_edited_by_user_a.save
# user b adds <ddd>
post_edited_by_user_b = Post.find(post.id)
post_edited_by_user_b.old_tag_string = "aaa bbb ccc"
post_edited_by_user_b.tag_string = "aaa bbb ccc ddd"
post_edited_by_user_b.save
# final should be <aaa>, <bbb>, <ddd>, <eee>
final_post = Post.find(post.id)
assert_equal(%w(aaa bbb ddd eee), TagQuery.scan(final_post.tag_string).sort)
end
should "merge any parent, source, and rating changes that were made after loading the initial set" do
post = create(:post, parent: nil, source: "", rating: "q")
parent_post = create(:post)
# user a changes rating to safe, adds parent
post_edited_by_user_a = Post.find(post.id)
post_edited_by_user_a.old_parent_id = ""
post_edited_by_user_a.old_source = ""
post_edited_by_user_a.old_rating = "q"
post_edited_by_user_a.parent_id = parent_post.id
post_edited_by_user_a.source = nil
post_edited_by_user_a.rating = "s"
post_edited_by_user_a.save
# user b adds source
post_edited_by_user_b = Post.find(post.id)
post_edited_by_user_b.old_parent_id = ""
post_edited_by_user_b.old_source = ""
post_edited_by_user_b.old_rating = "q"
post_edited_by_user_b.parent_id = nil
post_edited_by_user_b.source = "http://example.com"
post_edited_by_user_b.rating = "q"
post_edited_by_user_b.save
# final post should be rated safe and have the set parent and source
final_post = Post.find(post.id)
assert_equal(parent_post.id, final_post.parent_id)
assert_equal("https://example.com", final_post.source)
assert_equal("s", final_post.rating)
end
end
context "that has been tagged with a metatag" do
should "not include the metatag in its tag string" do
post = create(:post)
post.tag_string = "aaa pool:1234 pool:test rating:s fav:bob"
post.save
assert_equal("aaa", post.tag_string)
end
end
context "when validating tags" do
should "warn when creating a new general tag" do
@post.add_tag("tag")
@post.save
assert_match(/Created 1 new tag: \[\[tag\]\]/, @post.warnings.full_messages.join)
end
should "warn when adding an artist tag without an artist entry" do
@post.add_tag("artist:bkub")
@post.save
assert_match(/Artist \[\[bkub\]\] requires an artist entry./, @post.warnings.full_messages.join)
end
should "warn when a post from a known source is missing an artist tag" do
post = build(:post, source: "https://www.pixiv.net/member_illust.php?mode=medium&illust_id=65985331")
post.save
assert_match(/Artist tag is required/, post.warnings.full_messages.join)
end
should "warn when an upload doesn't have enough tags" do
post = create(:post, tag_string: "tagme")
assert_match(/Uploads must have at least \d+ general tags/, post.warnings.full_messages.join)
end
end
end
end
context "Updating:" do
context "an existing post" do
setup { @post = create(:post) }
should "call Tag.increment_post_counts with the correct params" do
@post.reload
Tag.expects(:increment_post_counts).once.with(["abc"])
@post.update(tag_string: "tag1 abc")
end
end
context "A rating unlocked post" do
setup { @post = create(:post) }
subject { @post }
should "not allow values S, safe, derp" do
["S", "safe", "derp"].each do |rating|
subject.rating = rating
assert(!subject.valid?)
end
end
should "allow values s, q, e" do
["s", "q", "e"].each do |rating|
subject.rating = rating
assert(subject.valid?)
end
end
end
context "A rating locked post" do
setup { @post = create(:post, is_rating_locked: true) }
subject { @post }
should "not allow values S, safe, derp" do
["S", "safe", "derp"].each do |rating|
subject.rating = rating
assert(!subject.valid?)
end
end
should "not allow values s, e" do
["s", "e"].each do |rating|
subject.rating = rating
assert(!subject.valid?)
end
end
end
end
context "Favorites:" do
context "Removing a post from a user's favorites" do
setup do
@user = create(:privileged_user)
@post = create(:post)
FavoriteManager.add!(user: @user, post: @post)
@user.reload
end
should "decrement the user's favorite_count" do
assert_difference("@user.reload.favorite_count", -1) do
FavoriteManager.remove!(user: @user, post: @post)
end
end
should "not decrement the post's score" do
@member = create(:user)
assert_no_difference("@post.score") { FavoriteManager.add!(user: @member, post: @post) }
assert_no_difference("@post.score") { FavoriteManager.remove!(user: @member, post: @post) }
end
should "not decrement the user's favorite_count if the user did not favorite the post" do
@post2 = create(:post)
assert_no_difference("@user.reload.favorite_count") do
FavoriteManager.remove!(user: @user, post: @post2)
end
end
end
context "Adding a post to a user's favorites" do
setup do
@user = create(:privileged_user)
@post = create(:post)
end
should "periodically clean the fav_string" do
@post.update_column(:fav_string, "fav:1 fav:1 fav:1")
@post.update_column(:fav_count, 3)
@post.append_user_to_fav_string(2)
assert_equal("fav:1 fav:2", @post.fav_string)
assert_equal(2, @post.fav_count)
end
# TODO: Needs to reload relationship to obtain non cached value
should "increment the user's favorite_count" do
assert_difference("@user.reload.favorite_count", 1) do
FavoriteManager.add!(user: @user, post: @post)
end
end
should "not increment the post's score" do
@member = create(:user)
FavoriteManager.add!(user: @user, post: @post)
assert_equal(0, @post.score)
end
should "update the fav strings on the post" do
FavoriteManager.add!(user: @user, post: @post)
@post.reload
assert_equal("fav:#{@user.id}", @post.fav_string)
assert(Favorite.exists?(:user_id => @user.id, :post_id => @post.id))
assert_raises(Favorite::Error) { FavoriteManager.add!(user: @user, post: @post) }
@post.reload
assert_equal("fav:#{@user.id}", @post.fav_string)
assert(Favorite.exists?(:user_id => @user.id, :post_id => @post.id))
FavoriteManager.remove!(user: @user, post: @post)
@post.reload
assert_equal("", @post.fav_string)
assert(!Favorite.exists?(:user_id => @user.id, :post_id => @post.id))
FavoriteManager.remove!(user: @user, post: @post)
@post.reload
assert_equal("", @post.fav_string)
assert(!Favorite.exists?(:user_id => @user.id, :post_id => @post.id))
end
end
context "Moving favorites to a parent post" do
setup do
@parent = create(:post)
@child = create(:post, parent: @parent)
@user1 = create(:user, enable_privacy_mode: true)
@privileged1 = create(:privileged_user)
@supervoter1 = create(:user)
FavoriteManager.add!(user: @user1, post: @child)
FavoriteManager.add!(user: @privileged1, post: @child)
FavoriteManager.add!(user: @supervoter1, post: @child)
FavoriteManager.add!(user: @supervoter1, post: @parent)
with_inline_jobs { @child.give_favorites_to_parent }
@child.reload
@parent.reload
end
should "move the favorites" do
assert_equal(0, @child.fav_count)
assert_equal(0, @child.favorites.count)
assert_equal("", @child.fav_string)
assert_equal([], @child.favorites.pluck(:user_id))
assert_equal(3, @parent.fav_count)
assert_equal(3, @parent.favorites.count)
end
end
end
context "Pools:" do
context "Removing a post from a pool" do
should "update the post's pool string" do
post = create(:post)
pool = create(:pool)
pool.add!(post)
pool.remove!(post)
post.reload
assert_equal("", post.pool_string)
pool.remove!(post)
post.reload
assert_equal("", post.pool_string)
end
end
context "Adding a post to a pool" do
should "update the post's pool string" do
post = create(:post)
pool = create(:pool)
pool.add!(post)
post.reload
assert_equal("pool:#{pool.id}", post.pool_string)
pool.add!(post)
post.reload
assert_equal("pool:#{pool.id}", post.pool_string)
pool.remove!(post)
post.reload
assert_equal("", post.pool_string)
end
end
end
context "Uploading:" do
context "Uploading a post" do
should "capture who uploaded the post" do
post = create(:post)
user1 = create(:user)
user2 = create(:user)
user3 = create(:user)
post.uploader = user1
assert_equal(user1.id, post.uploader_id)
post.uploader_id = user2.id
assert_equal(user2.id, post.uploader_id)
assert_equal(user2.id, post.uploader_id)
assert_equal(user2.name, post.uploader_name)
end
context "tag post counts" do
setup { @post = build(:post) }
should "call Tag.increment_post_counts with the correct params" do
Tag.expects(:increment_post_counts).once.with(["tag1", "tag2"])
@post.save
end
end
should "increment the uploaders post_upload_count" do
assert_difference(-> { CurrentUser.user.post_upload_count }) do
post = create(:post, uploader: CurrentUser.user)
CurrentUser.user.reload
end
end
end
end
context "Searching:" do
should "return posts for the age:<1minute tag" do
post = create(:post)
assert_tag_match([post], "age:<1minute")
end
should "return posts for the age:<1minute tag when the user is in Pacific time zone" do
post = create(:post)
Time.zone = "Pacific Time (US & Canada)"
assert_tag_match([post], "age:<1minute")
Time.zone = "Eastern Time (US & Canada)"
end
should "return posts for the age:<1minute tag when the user is in Tokyo time zone" do
post = create(:post)
Time.zone = "Asia/Tokyo"
assert_tag_match([post], "age:<1minute")
Time.zone = "Eastern Time (US & Canada)"
end
should "return posts for the ' tag" do
post1 = create(:post, tag_string: "'")
post2 = create(:post, tag_string: "aaa bbb")
assert_tag_match([post1], "'")
end
should "return posts for the ? tag" do
post1 = create(:post, tag_string: "?")
post2 = create(:post, tag_string: "aaa bbb")
assert_tag_match([post1], "?")
end
should "return posts for 1 tag" do
post1 = create(:post, tag_string: "aaa")
post2 = create(:post, tag_string: "aaa bbb")
post3 = create(:post, tag_string: "bbb ccc")
assert_tag_match([post2, post1], "aaa")
end
should "return posts for a 2 tag join" do
post1 = create(:post, tag_string: "aaa")
post2 = create(:post, tag_string: "aaa bbb")
post3 = create(:post, tag_string: "bbb ccc")
assert_tag_match([post2], "aaa bbb")
end
should "return posts for a 2 tag union" do
post1 = create(:post, tag_string: "aaa")
post2 = create(:post, tag_string: "aaab bbb")
post3 = create(:post, tag_string: "bbb ccc")
assert_tag_match([post3, post1], "~aaa ~ccc")
end
should "return posts for 1 tag with exclusion" do
post1 = create(:post, tag_string: "aaa")
post2 = create(:post, tag_string: "aaa bbb")
post3 = create(:post, tag_string: "bbb ccc")
assert_tag_match([post1], "aaa -bbb")
end
should "return posts for 1 tag with a pattern" do
post1 = create(:post, tag_string: "aaa")
post2 = create(:post, tag_string: "aaab bbb")
post3 = create(:post, tag_string: "bbb ccc")
assert_tag_match([post2, post1], "a*")
end
should "return posts for 2 tags, one with a pattern" do
post1 = create(:post, tag_string: "aaa")
post2 = create(:post, tag_string: "aaab bbb")
post3 = create(:post, tag_string: "bbb ccc")
assert_tag_match([post2], "a* bbb")
end
should "return posts for the id:<N> metatag" do
posts = create_list(:post, 3)
assert_tag_match([posts[1]], "id:#{posts[1].id}")
assert_tag_match([posts[2]], "id:>#{posts[1].id}")
assert_tag_match([posts[0]], "id:<#{posts[1].id}")
assert_tag_match([posts[2], posts[0]], "-id:#{posts[1].id}")
assert_tag_match([posts[2], posts[1]], "id:>=#{posts[1].id}")
assert_tag_match([posts[1], posts[0]], "id:<=#{posts[1].id}")
assert_tag_match([posts[2], posts[0]], "id:#{posts[0].id},#{posts[2].id}")
assert_tag_match(posts.reverse, "id:#{posts[0].id}..#{posts[2].id}")
end
should "return posts for the fav:<name> metatag" do
users = create_list(:user, 2)
posts = users.map do |u|
as(u) do
post = create(:post, tag_string: "abc")
FavoriteManager.add!(user: u, post: post)
post
end
end
assert_tag_match([posts[0]], "fav:#{users[0].name}")
assert_tag_match([posts[1]], "-fav:#{users[0].name}")
end
should "return posts for the pool:<name> metatag" do
create(:pool, name: "test_a")
create(:pool, name: "test_b")
post1 = create(:post, tag_string: "pool:test_a")
post2 = create(:post, tag_string: "pool:test_b")
assert_tag_match([post1], "pool:test_a")
assert_tag_match([post2], "-pool:test_a")
assert_tag_match([], "-pool:test_a -pool:test_b")
assert_tag_match([post2, post1], "pool:any")
assert_tag_match([], "pool:none")
end
should "return posts for the parent:<N> metatag" do
parent = create(:post)
child = create(:post, tag_string: "parent:#{parent.id}")
assert_tag_match([parent], "parent:none")
assert_tag_match([child], "-parent:none")
assert_tag_match([child], "parent:#{parent.id}")
assert_tag_match([child], "child:none")
assert_tag_match([parent], "child:any")
end
should "return posts for the user:<name> metatag" do
users = create_list(:user, 2)
posts = users.map { |u| create(:post, uploader: u) }
assert_tag_match([posts[0]], "user:#{users[0].name}")
assert_tag_match([posts[1]], "-user:#{users[0].name}")
end
should "return posts for the approver:<name> metatag" do
users = create_list(:user, 2)
posts = users.map { |u| create(:post, approver: u) }
posts << create(:post, approver: nil)
assert_tag_match([posts[0]], "approver:#{users[0].name}")
assert_tag_match([posts[2], posts[1]], "-approver:#{users[0].name}")
assert_tag_match([posts[1], posts[0]], "approver:any")
assert_tag_match([posts[2]], "approver:none")
end
should "return posts for the commenter:<name> metatag" do
users = create_list(:user, 2, created_at: 2.weeks.ago)
posts = create_list(:post, 2)
comms = users.zip(posts).map { |u, p| as(u) { create(:comment, post: p) } }
assert_tag_match([posts[0]], "commenter:#{users[0].name}")
assert_tag_match([posts[1]], "commenter:#{users[1].name}")
end
should "return posts for the commenter:<any|none> metatag" do
posts = create_list(:post, 2)
create(:comment, post: posts[0], is_hidden: false)
create(:comment, post: posts[1], is_hidden: true)
assert_tag_match([posts[0]], "commenter:any")
assert_tag_match([posts[1]], "commenter:none")
end
should "return posts for the noter:<name> metatag" do
users = create_list(:user, 2)
posts = create_list(:post, 2)
notes = users.zip(posts).map { |u, p| create(:note, creator: u, post: p) }
assert_tag_match([posts[0]], "noter:#{users[0].name}")
assert_tag_match([posts[1]], "noter:#{users[1].name}")
end
should "return posts for the noter:<any|none> metatag" do
posts = create_list(:post, 2)
create(:note, post: posts[0], is_active: true)
create(:note, post: posts[1], is_active: false)
assert_tag_match([posts[0]], "noter:any")
assert_tag_match([posts[1]], "noter:none")
end
should "return posts for the description:<text> metatag" do
posts = create_list(:post, 2)
posts[0].update_attribute(:description, 'abc')
posts[1].update_attribute(:description, 'efg')
assert_tag_match([posts[0]], "description:abc")
assert_tag_match([posts[1]], "description:efg")
end
should "return posts for the date:<d> metatag" do
post = create(:post, created_at: Time.parse("2017-01-01 12:00"))
assert_tag_match([post], "date:2017-01-01")
end
should "return posts for the age:<n> metatag" do
post = create(:post)
assert_tag_match([post], "age:<60")
assert_tag_match([post], "age:<60s")
assert_tag_match([post], "age:<1mi")
assert_tag_match([post], "age:<1h")
assert_tag_match([post], "age:<1d")
assert_tag_match([post], "age:<1w")
assert_tag_match([post], "age:<1mo")
assert_tag_match([post], "age:<1y")
end
should "return posts for the ratio:<x:y> metatag" do
post = create(:post, image_width: 1000, image_height: 500)
assert_tag_match([post], "ratio:2:1")
assert_tag_match([post], "ratio:2.0")
end
should "return posts for the status:<type> metatag" do
pending = create(:post, is_pending: true)
flagged = create(:post, is_flagged: true)
deleted = create(:post, is_deleted: true)
all = [deleted, flagged, pending]
assert_tag_match([flagged, pending], "status:modqueue")
assert_tag_match([pending], "status:pending")
assert_tag_match([flagged], "status:flagged")
assert_tag_match([deleted], "status:deleted")
assert_tag_match([], "status:active")
assert_tag_match(all, "status:any")
assert_tag_match(all, "status:all")
# TODO: These don't quite make sense, what should hide deleted posts and what shouldn't?
assert_tag_match(all - [deleted, flagged, pending], "-status:modqueue")
assert_tag_match(all - [deleted, pending], "-status:pending")
assert_tag_match(all - [deleted, flagged], "-status:flagged")
assert_tag_match(all - [deleted], "-status:deleted")
assert_tag_match(all, "-status:active")
end
should "return posts for the filetype:<ext> metatag" do
png = create(:post, file_ext: "png")
jpg = create(:post, file_ext: "jpg")
assert_tag_match([png], "filetype:png")
assert_tag_match([jpg], "-filetype:png")
end
should "return posts for the tagcount:<n> metatags" do
post = create(:post, tag_string: "artist:wokada copyright:vocaloid char:hatsune_miku twintails")
assert_tag_match([post], "tagcount:4")
assert_tag_match([], "tagcount:3")
assert_tag_match([post], "arttags:1")
assert_tag_match([post], "copytags:1")
assert_tag_match([post], "chartags:1")
assert_tag_match([post], "gentags:1")
assert_tag_match([], "gentags:0")
end
should "return posts for the md5:<md5> metatag" do
post1 = create(:post, md5: "abcd")
post2 = create(:post)
assert_tag_match([post1], "md5:abcd")
end
should "return posts for a source search" do
post1 = create(:post, source: "abcd")
post2 = create(:post, source: "abcdefg")
post3 = create(:post, source: "")
assert_tag_match([post2], "source:abcde")
assert_tag_match([post3, post1], "-source:abcde")
assert_tag_match([post3], "source:none")
assert_tag_match([post2, post1], "-source:none")
end
# TODO: Known broken. Need to normalize source during search and before index to fix bug with index creation.
should_eventually "return posts for a case insensitive source search" do
post1 = create(:post, source: "ABCD")
post2 = create(:post, source: "1234")
assert_tag_match([post1], "source:*abcd")
end
should "return posts for a pixiv source search" do
url = "http://i1.pixiv.net/img123/img/artist-name/789.png"
post = create(:post, source: url)
assert_tag_match([post], "source:*.pixiv.net/img*/artist-name/*")
assert_tag_match([], "source:*.pixiv.net/img*/artist-fake/*")
assert_tag_match([post], "source:https://*.pixiv.net/img*/img/artist-name/*")
assert_tag_match([], "source:https://*.pixiv.net/img*/img/artist-fake/*")
end
should "return posts for a rating:<s|q|e> metatag" do
s = create(:post, rating: "s")
q = create(:post, rating: "q")
e = create(:post, rating: "e")
all = [e, q, s]
assert_tag_match([s], "rating:s")
assert_tag_match([q], "rating:q")
assert_tag_match([e], "rating:e")
assert_tag_match(all - [s], "-rating:s")
assert_tag_match(all - [q], "-rating:q")
assert_tag_match(all - [e], "-rating:e")
end
should "return posts for a locked:<rating|note|status> metatag" do
rating_locked = create(:post, is_rating_locked: true)
note_locked = create(:post, is_note_locked: true)
status_locked = create(:post, is_status_locked: true)
all = [status_locked, note_locked, rating_locked]
assert_tag_match([rating_locked], "locked:rating")
assert_tag_match([note_locked], "locked:note")
assert_tag_match([status_locked], "locked:status")
assert_tag_match(all - [rating_locked], "-locked:rating")
assert_tag_match(all - [note_locked], "-locked:note")
assert_tag_match(all - [status_locked], "-locked:status")
end
should "return posts for a upvote:<user>, downvote:<user> metatag" do
old_user = create(:mod_user, created_at: 5.days.ago)
as(old_user) do
upvoted = create(:post, tag_string: "abc")
downvoted = create(:post, tag_string: "abc")
VoteManager.vote!(user: CurrentUser.user, post: upvoted, score: 1)
VoteManager.vote!(user: CurrentUser.user, post: downvoted, score: -1)
assert_tag_match([upvoted], "upvote:#{CurrentUser.name}")
assert_tag_match([downvoted], "downvote:#{CurrentUser.name}")
end
end
# FIXME: This test fails randomly at different assertions
should_eventually "return posts ordered by a particular attribute" do
posts = (1..2).map do |n|
tags = ["tagme", "gentag1 gentag2 artist:arttag char:chartag copy:copytag"]
p = create(
:post,
score: n,
fav_count: n,
file_size: 1.megabyte * n,
# posts[0] is portrait, posts[1] is landscape. posts[1].mpixels > posts[0].mpixels.
image_height: 100*n*n,
image_width: 100*(3-n)*n,
tag_string: tags[n-1],
)
create(:comment, post: p, do_not_bump_post: false)
create(:note, post: p)
p
end
create(:note, post: posts.second)
assert_tag_match(posts.reverse, "order:id_desc")
assert_tag_match(posts.reverse, "order:score")
assert_tag_match(posts.reverse, "order:favcount")
assert_tag_match(posts.reverse, "order:change")
assert_tag_match(posts.reverse, "order:comment")
assert_tag_match(posts.reverse, "order:comment_bumped")
assert_tag_match(posts.reverse, "order:note")
assert_tag_match(posts.reverse, "order:mpixels")
assert_tag_match(posts.reverse, "order:portrait")
assert_tag_match(posts.reverse, "order:filesize")
assert_tag_match(posts.reverse, "order:tagcount")
assert_tag_match(posts.reverse, "order:gentags")
assert_tag_match(posts.reverse, "order:arttags")
assert_tag_match(posts.reverse, "order:chartags")
assert_tag_match(posts.reverse, "order:copytags")
assert_tag_match(posts.reverse, "order:rank")
assert_tag_match(posts.reverse, "order:note_count")
assert_tag_match(posts.reverse, "order:note_count_desc")
assert_tag_match(posts.reverse, "order:notes")
assert_tag_match(posts.reverse, "order:notes_desc")
assert_tag_match(posts, "order:id_asc")
assert_tag_match(posts, "order:score_asc")
assert_tag_match(posts, "order:favcount_asc")
assert_tag_match(posts, "order:change_asc")
assert_tag_match(posts, "order:comment_asc")
assert_tag_match(posts, "order:comment_bumped_asc")
assert_tag_match(posts, "order:note_asc")
assert_tag_match(posts, "order:mpixels_asc")
assert_tag_match(posts, "order:landscape")
assert_tag_match(posts, "order:filesize_asc")
assert_tag_match(posts, "order:tagcount_asc")
assert_tag_match(posts, "order:gentags_asc")
assert_tag_match(posts, "order:arttags_asc")
assert_tag_match(posts, "order:chartags_asc")
assert_tag_match(posts, "order:copytags_asc")
assert_tag_match(posts, "order:note_count_asc")
assert_tag_match(posts, "order:notes_asc")
end
should "return posts for order:comment_bumped" do
post1 = create(:post)
post2 = create(:post)
post3 = create(:post)
as(create(:privileged_user)) do
create(:comment, post: post1)
create(:comment, post: post2, do_not_bump_post: true)
create(:comment, post: post3)
end
assert_tag_match([post3, post1], "order:comment_bumped")
assert_tag_match([post1, post3], "order:comment_bumped_asc")
create(:comment, post: post2)
assert_tag_match([post2, post3, post1], "order:comment_bumped")
assert_tag_match([post1, post3, post2], "order:comment_bumped_asc")
end
should "return posts for a filesize search" do
post = create(:post, file_size: 1.megabyte)
assert_tag_match([post], "filesize:1mb")
assert_tag_match([post], "filesize:1000kb")
assert_tag_match([post], "filesize:1048576b")
end
should "not count free tags against the user's search limit" do
post1 = create(:post, tag_string: "aaa bbb rating:s")
Danbooru.config.expects(:is_unlimited_tag?).with("rating:s").once.returns(true)
Danbooru.config.expects(:is_unlimited_tag?).with(anything).twice.returns(false)
assert_tag_match([post1], "aaa bbb rating:s")
end
should "succeed for exclusive tag searches with no other tag" do
post1 = create(:post, rating: "s", tag_string: "aaa")
assert_nothing_raised do
relation = Post.tag_match("-aaa")
end
end
should "succeed for exclusive tag searches combined with a metatag" do
post1 = create(:post, rating: "s", tag_string: "aaa")
assert_nothing_raised do
relation = Post.tag_match("-aaa id:>0")
end
end
should "return posts for replacements" do
assert_tag_match([], "pending_replacements:true")
assert_tag_match([], "pending_replacements:false")
post = create(:post)
replacement = create(:png_replacement, creator: @user, post: post)
assert_tag_match([post], "pending_replacements:true")
end
should "return no posts when the replacement is not pending anymore" do
post1 = create(:post)
upload = UploadService.new(attributes_for(:upload).merge(file: fixture_file_upload("test.gif"), uploader: @user, tag_string: "tst")).start!
post2 = upload.post
post3 = create(:post)
post4 = create(:post)
replacement1 = create(:png_replacement, creator: @user, post: post1)
replacement1.reject!
replacement2 = create(:png_replacement, creator: @user, post: post2)
replacement2.approve!(penalize_current_uploader: true)
replacement3 = create(:jpg_replacement, creator: @user, post: post3)
promoted_post = replacement3.promote!.post
replacement4 = create(:webm_replacement, creator: @user, post: post4)
replacement4.destroy!
assert_tag_match([], "pending_replacements:true")
assert_tag_match([promoted_post, post4, post3, post2, post1], "pending_replacements:false")
end
end
context "Voting:" do
should "not allow duplicate votes" do
user = create(:privileged_user)
post = create(:post)
as(user) do
assert_nothing_raised { VoteManager.vote!(user: user, post: post, score: 1) }
# Need unvote is returned upon duplicates that are accounted for.
assert_equal(:need_unvote, VoteManager.vote!(user: user, post: post, score: 1) )
post.reload
assert_equal(1, PostVote.count)
assert_equal(1, post.score)
end
end
should "allow undoing of votes" do
user = create(:privileged_user, created_at: 7.days.ago)
post = create(:post)
# We deliberately don't call post.reload until the end to verify that
# post.unvote! returns the correct score even when not forcibly reloaded.
as(user) do
VoteManager.vote!(post: post, user: user, score: 1)
assert_equal(1, post.score)
VoteManager.unvote!(post: post, user: user)
assert_equal(0, post.score)
assert_nothing_raised { VoteManager.vote!(post: post, user: user, score: -1) }
assert_equal(-1, post.score)
VoteManager.unvote!(post: post, user: user)
assert_equal(0, post.score)
assert_nothing_raised { VoteManager.vote!(post: post, user: user, score: 1) }
assert_equal(1, post.score)
post.reload
assert_equal(1, post.score)
end
end
end
# TODO: These are pretty messed up, both structurally, and expectation wise.
# New codebase uses fewer cached items, and the keys are different because of -status:deleted default
# Most of these need major refactoring.
# context "Counting:" do
# context "Creating a post" do
# setup do
# create(:tag_alias, antecedent_name: "alias", consequent_name: "aaa")
# create(:post, tag_string: "aaa", score: 42)
# end
#
# context "a single metatag" do
# should "return the correct cached count" do
# build(:tag, name: "score:42", post_count: -100).save(validate: false)
# Post.set_count_in_cache("score:42", 100)
#
# assert_equal(100, Post.fast_count("score:42"))
# end
#
# should "return the correct cached count for a pool:<id> search" do
# build(:tag, name: "pool:1234", post_count: -100).save(validate: false)
# Post.set_count_in_cache("pool:1234", 100)
#
# assert_equal(100, Post.fast_count("pool:1234"))
# end
# end
#
# context "a multi-tag search" do
# should "return the cached count, if it exists" do
# Post.set_count_in_cache("aaa score:42", 100)
# assert_equal(100, Post.fast_count("aaa score:42"))
# end
#
# should "return the true count, if not cached" do
# assert_equal(1, Post.fast_count("aaa score:42"))
# end
#
# should "set the expiration time" do
# Cache.expects(:put).with(Post.count_cache_key("aaa score:42"), 1, 180)
# Post.fast_count("aaa score:42")
# end
# end
#
# context "a blank search" do
# should "should execute a search" do
# Cache.delete(Post.count_cache_key(''))
# Post.expects(:fast_count_search).with("", kind_of(Hash)).once.returns(1)
# assert_equal(1, Post.fast_count(""))
# end
#
# should "set the value in cache" do
# Post.expects(:set_count_in_cache).with("", kind_of(Integer)).once
# Post.fast_count("")
# end
#
# context "with a primed cache" do
# setup do
# Cache.write(Post.count_cache_key(''), "100")
# end
#
# should "fetch the value from the cache" do
# assert_equal(100, Post.fast_count(""))
# end
# end
#
# should "translate an alias" do
# assert_equal(1, Post.fast_count("alias"))
# end
#
# should "return 0 for a nonexisting tag" do
# assert_equal(0, Post.fast_count("bbb"))
# end
#
# context "in safe mode" do
# setup do
# CurrentUser.stubs(:safe_mode?).returns(true)
# create(:post, rating: "s")
# end
#
# should "work for a blank search" do
# assert_equal(1, Post.fast_count(""))
# end
#
# should "work for a nil search" do
# assert_equal(1, Post.fast_count(nil))
# end
#
# should "not fail for a two tag search by a member" do
# post1 = create(:post, tag_string: "aaa bbb rating:s")
# post2 = create(:post, tag_string: "aaa bbb rating:e")
#
# Danbooru.config.expects(:is_unlimited_tag?).with("rating:s").once.returns(true)
# Danbooru.config.expects(:is_unlimited_tag?).with(anything).twice.returns(false)
# assert_equal(1, Post.fast_count("aaa bbb"))
# end
#
# should "set the value in cache" do
# Post.expects(:set_count_in_cache).with("rating:s", kind_of(Integer)).once
# Post.fast_count("")
# end
#
# context "with a primed cache" do
# setup do
# Cache.write(Post.count_cache_key('rating:s'), "100")
# end
#
# should "fetch the value from the cache" do
# assert_equal(100, Post.fast_count(""))
# end
# end
# end
# end
# end
# end
context "Reverting: " do
context "a post that is rating locked" do
setup do
@post = create(:post, rating: "s")
@post.update(rating: "q", is_rating_locked: true)
end
should "not revert the rating" do
assert_raises ActiveRecord::RecordInvalid do
@post.revert_to!(@post.versions.first)
end
assert_equal(["Rating is locked and cannot be changed. Unlock the post first."], @post.errors.full_messages)
assert_equal(@post.versions.last.rating, @post.reload.rating)
end
should "revert the rating after unlocking" do
@post.update(rating: "e", is_rating_locked: false)
assert_nothing_raised do
@post.revert_to!(@post.versions.first)
end
assert(@post.valid?)
assert_equal(@post.versions.first.rating, @post.rating)
end
end
context "a post that has been updated" do
setup do
@post = create(:post, rating: "q", tag_string: "aaa", source: "")
@post.reload
@post.update(:tag_string => "aaa bbb ccc ddd")
@post.reload
@post.update(:tag_string => "bbb xxx yyy", :source => "xyz")
@post.reload
@post.update(:tag_string => "bbb mmm yyy", :source => "abc")
@post.reload
end
context "and then reverted to an early version" do
setup do
@version = @post.versions[1]
@post.revert_to!(@version)
@post.reload
end
should "correctly revert all fields" do
assert_equal("aaa bbb ccc ddd", @post.tag_string)
assert_equal("", @post.source)
assert_equal("q", @post.rating)
assert_equal("Revert to version #{@version.version}", @post.versions.last.reason)
end
end
context "and then reverted to a later version" do
setup do
@post.revert_to(@post.versions[-2])
end
should "correctly revert all fields" do
assert_equal("bbb xxx yyy", @post.tag_string)
assert_equal("xyz", @post.source)
assert_equal("q", @post.rating)
end
end
end
end
context "URLs:" do
should "generate the correct urls for animated gifs" do
@post = build(:post, md5: "deadbeef", file_ext: "gif", tag_string: "animated_gif")
assert_equal("#{Danbooru.config.hostname}/data/preview/deadbeef.jpg", @post.preview_file_url)
assert_equal("#{Danbooru.config.hostname}/data/deadbeef.gif", @post.large_file_url)
assert_equal("#{Danbooru.config.hostname}/data/deadbeef.gif", @post.file_url)
end
end
context "Notes:" do
context "#copy_notes_to" do
setup do
@src = create(:post, image_width: 100, image_height: 100, tag_string: "translated partially_translated")
@dst = create(:post, image_width: 200, image_height: 200, tag_string: "translation_request")
@src.notes.create(x: 10, y: 10, width: 10, height: 10, body: "test")
@src.notes.create(x: 10, y: 10, width: 10, height: 10, body: "deleted", is_active: false)
@src.reload
@src.copy_notes_to(@dst)
end
should "copy notes and tags" do
assert_equal(1, @dst.notes.active.length)
assert_equal("low_res partially_translated thumbnail translated", @dst.tag_string)
end
should "rescale notes" do
note = @dst.notes.active.first
assert_equal([20, 20, 20, 20], [note.x, note.y, note.width, note.height])
end
end
end
end