Files
danbooru/test/unit/bulk_update_request_test.rb
evazion 6275e85148 BURs: refactor implication wiki page validations.
Move the validation that the tags in an implication must have wiki pages
back into the TagImplication model. Use validation contexts to only run
the validation when the BUR is created, not when the BUR is approved.
2020-12-02 14:30:18 -06:00

483 lines
18 KiB
Ruby

require 'test_helper'
class BulkUpdateRequestTest < ActiveSupport::TestCase
def create_bur!(script, approver)
bur = create(:bulk_update_request, script: script)
perform_enqueued_jobs { bur.approve!(approver) }
bur
end
context "a bulk update request" do
setup do
@admin = FactoryBot.create(:admin_user)
CurrentUser.user = @admin
CurrentUser.ip_addr = "127.0.0.1"
end
teardown do
CurrentUser.user = nil
CurrentUser.ip_addr = nil
end
should_eventually "parse tags with tag type prefixes inside the script" do
@bur = create(:bulk_update_request, script: "mass update aaa -> artist:bbb")
assert_equal(%w[aaa bbb], @bur.tags)
end
context "when approving a BUR" do
context "the category command" do
should "change the tag's category" do
@tag = create(:tag, name: "hello")
@bur = create_bur!("category hello -> artist", @admin)
assert_equal(true, @bur.valid?)
assert_equal(true, @tag.reload.artist?)
end
should "fail if the tag doesn't already exist" do
@bur = build(:bulk_update_request, script: "category hello -> artist")
assert_equal(false, @bur.valid?)
assert_equal(["Can't change category hello -> artist (the 'hello' tag doesn't exist)"], @bur.errors[:base])
end
end
context "the create alias command" do
setup do
@wiki = create(:wiki_page, title: "foo")
@artist = create(:artist, name: "foo")
@bur = create_bur!("create alias foo -> bar", @admin)
end
should "create an alias" do
@alias = TagAlias.find_by(antecedent_name: "foo", consequent_name: "bar")
assert_equal(true, @alias.present?)
assert_equal(true, @alias.is_active?)
end
should "rename the aliased tag's artist entry and wiki page" do
assert_equal("bar", @artist.reload.name)
assert_equal("bar", @wiki.reload.title)
end
should "move any active aliases from the old tag to the new tag" do
@bur1 = create_bur!("alias aaa -> bbb", @admin)
@bur2 = create_bur!("alias bbb -> ccc", @admin)
assert_equal(false, TagAlias.where(antecedent_name: "aaa", consequent_name: "bbb", status: "active").exists?)
assert_equal(true, TagAlias.where(antecedent_name: "bbb", consequent_name: "ccc", status: "active").exists?)
assert_equal(true, TagAlias.where(antecedent_name: "aaa", consequent_name: "ccc", status: "active").exists?)
end
should "move any active implications from the old tag to the new tag" do
@bur1 = create_bur!("imply aaa -> bbb", @admin)
@bur2 = create_bur!("alias bbb -> ccc", @admin)
assert_equal(false, TagImplication.where(antecedent_name: "aaa", consequent_name: "bbb", status: "active").exists?)
assert_equal(true, TagImplication.where(antecedent_name: "aaa", consequent_name: "ccc", status: "active").exists?)
@bur3 = create_bur!("alias aaa -> ddd", @admin)
assert_equal(false, TagImplication.where(antecedent_name: "aaa", consequent_name: "ccc", status: "active").exists?)
assert_equal(true, TagImplication.where(antecedent_name: "ddd", consequent_name: "ccc", status: "active").exists?)
end
should "allow aliases to be reversed in one step" do
@alias = create(:tag_alias, antecedent_name: "aaa", consequent_name: "bbb")
@bur = create_bur!("create alias bbb -> aaa", @admin)
assert_equal(true, @alias.reload.is_deleted?)
assert_equal(true, TagAlias.active.exists?(antecedent_name: "bbb", consequent_name: "aaa"))
end
should "fail if the alias is invalid" do
@alias = create(:tag_alias, antecedent_name: "bbb", consequent_name: "ccc")
@bur = build(:bulk_update_request, script: "create alias aaa -> bbb")
assert_equal(false, @bur.valid?)
assert_equal(["Can't create alias aaa -> bbb (bbb is already aliased to ccc)"], @bur.errors.full_messages)
end
should "be case-insensitive" do
@bur = create_bur!("CREATE ALIAS AAA -> BBB", @admin)
@alias = TagAlias.find_by(antecedent_name: "aaa", consequent_name: "bbb")
assert_equal(true, @alias.present?)
assert_equal(true, @alias.is_active?)
end
end
context "the create implication command" do
should "create an implication" do
@bur = create_bur!("create implication foo -> bar", @admin)
@implication = TagImplication.find_by(antecedent_name: "foo", consequent_name: "bar")
assert_equal(true, @implication.present?)
assert_equal(true, @implication.is_active?)
end
should "fail for an implication that is redundant with an existing implication" do
create(:tag_implication, antecedent_name: "a", consequent_name: "b")
create(:tag_implication, antecedent_name: "b", consequent_name: "c")
@bur = build(:bulk_update_request, script: "imply a -> c")
assert_equal(false, @bur.valid?)
assert_equal(["Can't create implication a -> c (a already implies c through another implication)"], @bur.errors.full_messages)
end
should "fail for an implication that is a duplicate of an existing implication" do
create(:tag_implication, antecedent_name: "a", consequent_name: "b")
@bur = build(:bulk_update_request, script: "imply a -> b")
assert_equal(false, @bur.valid?)
assert_equal(["Can't create implication a -> b (Implication already exists)"], @bur.errors.full_messages)
end
should "fail for an implication that is redundant with another implication in the same BUR" do
create(:tag_implication, antecedent_name: "b", consequent_name: "c")
@bur = build(:bulk_update_request, script: "imply a -> b\nimply a -> c")
assert_equal(false, @bur.valid?)
assert_equal(["Can't create implication a -> c (a already implies c through another implication)"], @bur.errors.full_messages)
end
end
context "the remove alias command" do
should "remove an alias" do
create(:tag_alias, antecedent_name: "foo", consequent_name: "bar")
@bur = create_bur!("remove alias foo -> bar", @admin)
@alias = TagAlias.find_by(antecedent_name: "foo", consequent_name: "bar")
assert_equal(true, @alias.present?)
assert_equal(true, @alias.is_deleted?)
end
should "fail if the alias isn't active" do
create(:tag_alias, antecedent_name: "foo", consequent_name: "bar", status: "deleted")
@bur = build(:bulk_update_request, script: "remove alias foo -> bar")
assert_equal(false, @bur.valid?)
assert_equal(["Can't remove alias foo -> bar (alias doesn't exist)"], @bur.errors[:base])
end
should "fail if the alias doesn't already exist" do
@bur = build(:bulk_update_request, script: "remove alias foo -> bar")
assert_equal(false, @bur.valid?)
assert_equal(["Can't remove alias foo -> bar (alias doesn't exist)"], @bur.errors[:base])
end
end
context "the remove implication command" do
should "remove an implication" do
create(:tag_implication, antecedent_name: "foo", consequent_name: "bar", status: "active")
@bur = create_bur!("remove implication foo -> bar", @admin)
@implication = TagImplication.find_by(antecedent_name: "foo", consequent_name: "bar")
assert_equal(true, @implication.present?)
assert_equal(true, @implication.is_deleted?)
end
should "fail if the implication isn't active" do
create(:tag_implication, antecedent_name: "foo", consequent_name: "bar", status: "deleted")
@bur = build(:bulk_update_request, script: "remove implication foo -> bar")
assert_equal(false, @bur.valid?)
assert_equal(["Can't remove implication foo -> bar (implication doesn't exist)"], @bur.errors[:base])
end
should "fail if the implication doesn't already exist" do
@bur = build(:bulk_update_request, script: "remove implication foo -> bar")
assert_equal(false, @bur.valid?)
assert_equal(["Can't remove implication foo -> bar (implication doesn't exist)"], @bur.errors[:base])
end
end
context "the mass update command" do
setup do
@post = create(:post, tag_string: "foo")
@bur = create_bur!("mass update foo -> bar", @admin)
end
should "update the tags" do
assert_equal("bar", @post.reload.tag_string)
end
should "be case-sensitive" do
@post = create(:post, source: "imageboard")
@bur = create_bur!("mass update source:imageboard -> source:Imageboard", @admin)
assert_equal("Imageboard", @post.reload.source)
end
end
context "the rename command" do
setup do
@artist = create(:artist, name: "foo")
@wiki = create(:wiki_page, title: "foo", body: "[[foo]]")
@post = create(:post, tag_string: "foo blah")
@bur = create_bur!("rename foo -> bar", @admin)
end
should "rename the tags" do
assert_equal("approved", @bur.status)
assert_equal("bar blah", @post.reload.tag_string)
end
should "move the tag's artist entry and wiki page" do
assert_equal("bar", @artist.reload.name)
assert_equal("bar", @wiki.reload.title)
assert_equal("[[bar]]", @wiki.body)
end
should "fail if the old tag doesn't exist" do
@bur = build(:bulk_update_request, script: "rename aaa -> bbb")
assert_equal(false, @bur.valid?)
assert_equal(["Can't rename aaa -> bbb (the 'aaa' tag doesn't exist)"], @bur.errors.full_messages)
end
context "when renaming a character tag with a *_(cosplay) tag" do
should "move the *_(cosplay) tag as well" do
@post = create(:post, tag_string: "toosaka_rin_(cosplay)")
@wiki = create(:wiki_page, title: "toosaka_rin_(cosplay)")
@ta = create(:tag_alias, antecedent_name: "toosaka_rin", consequent_name: "tohsaka_rin")
create_bur!("rename toosaka_rin -> tohsaka_rin", @admin)
assert_equal("cosplay tohsaka_rin tohsaka_rin_(cosplay)", @post.reload.tag_string)
assert_equal("tohsaka_rin_(cosplay)", @wiki.reload.title)
end
end
end
context "the nuke command" do
should "remove tags" do
@p1 = create(:post, tag_string: "foo")
@p2 = create(:post, tag_string: "bar")
@bur = create_bur!("nuke foo", @admin)
assert_equal("tagme", @p1.reload.tag_string)
end
should "remove pools" do
@pool = create(:pool)
@post = create(:post, tag_string: "bar pool:#{@pool.id}")
@bur = create_bur!("nuke pool:#{@pool.id}", @admin)
assert_equal([], @pool.post_ids)
end
end
context "that contains a mass update followed by an alias" do
should "make the alias take effect after the mass update" do
@p1 = create(:post, tag_string: "maid_dress")
@p2 = create(:post, tag_string: "maid")
create_bur!("mass update maid_dress -> maid dress\nalias maid_dress -> maid", @admin)
assert_equal("dress maid", @p1.reload.tag_string)
assert_equal("maid", @p2.reload.tag_string)
end
end
context "that reverses an alias by removing and recreating it" do
should "not fail with an alias conflict" do
@ta = create(:tag_alias, antecedent_name: "rabbit", consequent_name: "bunny")
create_bur!("unalias rabbit -> bunny\nalias bunny -> rabbit", @admin)
assert_equal("deleted", @ta.reload.status)
assert_equal("active", TagAlias.find_by(antecedent_name: "bunny", consequent_name: "rabbit").status)
end
end
end
context "when validating a script" do
context "an unparseable script" do
should "fail validation" do
@script = <<~EOS
create alias aaa -> 000
create alias bbb > 111
create alias ccc -> 222
EOS
@bur = build(:bulk_update_request, script: @script)
assert_equal(false, @bur.valid?)
assert_equal(["Invalid line: create alias bbb > 111"], @bur.errors[:base])
end
end
context "a script with extra whitespace" do
should "validate" do
@script = %{
create alias aaa -> 000
create alias bbb -> 111
}
@bur = create(:bulk_update_request, script: @script)
assert_equal(true, @bur.valid?)
end
end
context "requesting an implication for an empty tag without a wiki" do
should "succeed" do
@bur = create(:bulk_update_request, script: "imply a -> b")
assert_equal(true, @bur.valid?)
end
end
context "requesting an implication for a populated tag without a wiki" do
should "fail" do
create(:tag, name: "a", post_count: 1)
create(:tag, name: "b", post_count: 1)
@bur = build(:bulk_update_request, script: "imply a -> b")
assert_equal(false, @bur.valid?)
assert_equal(["Can't create implication a -> b ('a' must have a wiki page; 'b' must have a wiki page)"], @bur.errors.full_messages)
end
end
end
context "when the script is updated" do
should "update the BUR's list of affected tags" do
create(:tag_alias, antecedent_name: "ccc", consequent_name: "222")
create(:tag_implication, antecedent_name: "ddd", consequent_name: "333")
create(:tag, name: "iii")
@script = <<~EOS
create alias aaa -> 000
create implication bbb -> 111
remove alias ccc -> 222
remove implication ddd -> 333
mass update eee id:1 -fff ~ggg hhh* -> 444 -555
category iii -> meta
EOS
@bur = create(:bulk_update_request, script: "create alias aaa -> bbb")
assert_equal(%w[aaa bbb], @bur.tags)
@bur.update!(script: @script)
assert_equal(%w(000 111 222 333 444 aaa bbb ccc ddd eee iii), @bur.tags)
end
end
context "on approval" do
setup do
@post = create(:post, tag_string: "foo aaa")
@script = '
create alias foo -> bar
create implication bar -> baz
mass update aaa -> bbb
'
@bur = create_bur!(@script, @admin)
@ta = TagAlias.where(:antecedent_name => "foo", :consequent_name => "bar").first
@ti = TagImplication.where(:antecedent_name => "bar", :consequent_name => "baz").first
end
should "reference the approver in the automated message" do
assert_match(Regexp.compile(@admin.name), @bur.forum_post.body)
end
should "set the BUR approver" do
assert_equal(@admin.id, @bur.approver.id)
end
should "create aliases/implications" do
assert_equal("active", @ta.status)
assert_equal("active", @ti.status)
end
should "process mass updates" do
assert_equal("bar baz bbb", @post.reload.tag_string)
end
should "set the alias/implication approvers" do
assert_equal(@admin.id, @ta.approver.id)
assert_equal(@admin.id, @ti.approver.id)
end
end
should "create a forum topic" do
bur = create(:bulk_update_request, reason: "zzz", script: "create alias aaa -> bbb")
assert_equal(true, bur.forum_post.present?)
assert_match(/\[bur:#{bur.id}\]/, bur.forum_post.body)
assert_match(/zzz/, bur.forum_post.body)
end
context "with an associated forum topic" do
setup do
@topic = create(:forum_topic, title: "[bulk] hoge", creator: @admin)
@post = create(:forum_post, topic: @topic, creator: @admin)
@req = FactoryBot.create(:bulk_update_request, :script => "create alias AAA -> BBB", :forum_topic_id => @topic.id, :forum_post_id => @post.id, :title => "[bulk] hoge")
end
should "gracefully handle validation errors during approval" do
@req.stubs(:update!).raises(BulkUpdateRequestProcessor::Error.new("blah"))
assert_difference("ForumPost.count", 1) do
@req.approve!(@admin)
end
assert_equal("pending", @req.reload.status)
end
should "leave the BUR pending if there is an unexpected error during approval" do
@req.forum_updater.stubs(:update).raises(RuntimeError.new("blah"))
assert_raises(RuntimeError) { @req.approve!(@admin) }
# XXX Raises "Couldn't find BulkUpdateRequest without an ID". Possible
# rails bug? (cf rails #34637, #34504, #30167, #15018).
# @req.reload
@req = BulkUpdateRequest.find(@req.id)
assert_equal("pending", @req.status)
end
should "update the topic when processed" do
assert_difference("ForumPost.count") do
@req.approve!(@admin)
end
assert_match(/approved/, @post.reload.body)
end
should "update the topic when rejected" do
@req.approver_id = @admin.id
assert_difference("ForumPost.count") do
@req.reject!(@admin)
end
assert_match(/rejected/, @post.reload.body)
end
should "reference the rejector in the automated message" do
@req.reject!(@admin)
assert_match(Regexp.compile(@admin.name), @req.forum_post.body)
end
should "not send @mention dmails to the approver" do
assert_no_difference("Dmail.count") do
@req.approve!(@admin)
end
end
end
context "when searching" do
setup do
@bur1 = create(:bulk_update_request, title: "foo", script: "create alias aaa -> bbb", user: @admin, approver: @admin, status: "approved")
@bur2 = create(:bulk_update_request, title: "bar", script: "create implication bbb -> ccc", user: @admin)
end
should "work" do
assert_equal([@bur2.id, @bur1.id], BulkUpdateRequest.search.map(&:id))
assert_equal([@bur1.id], BulkUpdateRequest.search(user_name: @admin.name, approver_name: @admin.name, status: "approved").map(&:id))
end
end
end
end