BURs: clean up parsing and error handling.
* Don't raise exceptions when a BUR is invalid. Instead, use Rails validations to return errors. Fixes invalid BURs potentially raising exceptions in views. Also makes it so that each error in a BUR is reported, not just the first one. * Revalidate the BUR whenever the script is edited, not just when the BUR is created. Ensures the BUR can't be broken by editing. Fixes a bug where forum threads could be broken by someone editing a BUR and breaking the syntax, thereby causing the BUR to raise an unparseable script error when the forum thread was viewed. * Validate that removed aliases and implication actually exist. * Validate that the tag actually exists when changing a tag's category. * Combine bulk update request processor unit tests with main bulk update request unit tests.
This commit is contained in:
@@ -1,109 +1,127 @@
|
||||
class BulkUpdateRequestProcessor
|
||||
extend Memoist
|
||||
include ActiveModel::Validations
|
||||
|
||||
class Error < StandardError; end
|
||||
attr_accessor :text, :forum_topic_id, :skip_secondary_validations
|
||||
|
||||
def initialize(text, forum_topic_id: nil, skip_secondary_validations: true)
|
||||
@forum_topic_id = forum_topic_id
|
||||
@text = text
|
||||
@skip_secondary_validations = skip_secondary_validations
|
||||
attr_reader :bulk_update_request
|
||||
delegate :script, :forum_topic_id, :skip_secondary_validations, to: :bulk_update_request
|
||||
validate :validate_script
|
||||
|
||||
def initialize(bulk_update_request)
|
||||
@bulk_update_request = bulk_update_request
|
||||
end
|
||||
|
||||
def tokens
|
||||
text.split(/\r\n|\r|\n/).reject(&:blank?).map do |line|
|
||||
# Parse the script into a list of commands.
|
||||
def commands
|
||||
script.split(/\r\n|\r|\n/).reject(&:blank?).map do |line|
|
||||
line = line.gsub(/[[:space:]]+/, " ").strip
|
||||
next if line.empty?
|
||||
|
||||
if line =~ /^(?:create alias|aliasing|alias) (\S+) -> (\S+)$/i
|
||||
case line
|
||||
when /\A(?:create alias|aliasing|alias) (\S+) -> (\S+)\z/i
|
||||
[:create_alias, $1, $2]
|
||||
elsif line =~ /^(?:create implication|implicating|implicate|imply) (\S+) -> (\S+)$/i
|
||||
when /\A(?:create implication|implicating|implicate|imply) (\S+) -> (\S+)\z/i
|
||||
[:create_implication, $1, $2]
|
||||
elsif line =~ /^(?:remove alias|unaliasing|unalias) (\S+) -> (\S+)$/i
|
||||
when /\A(?:remove alias|unaliasing|unalias) (\S+) -> (\S+)\z/i
|
||||
[:remove_alias, $1, $2]
|
||||
elsif line =~ /^(?:remove implication|unimplicating|unimplicate|unimply) (\S+) -> (\S+)$/i
|
||||
when /\A(?:remove implication|unimplicating|unimplicate|unimply) (\S+) -> (\S+)\z/i
|
||||
[:remove_implication, $1, $2]
|
||||
elsif line =~ /^(?:mass update|updating|update|change) (.+?) -> (.*)$/i
|
||||
when /\A(?:mass update|updating|update|change) (.+?) -> (.*)\z/i
|
||||
[:mass_update, $1, $2]
|
||||
elsif line =~ /^category (\S+) -> (#{Tag.categories.regexp})/
|
||||
when /\Acategory (\S+) -> (#{Tag.categories.regexp})\z/i
|
||||
[:change_category, $1, $2]
|
||||
elsif line.strip.empty?
|
||||
# do nothing
|
||||
else
|
||||
raise Error, "Unparseable line: #{line}"
|
||||
[:invalid_line, line]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def validate!
|
||||
tokens.map do |token|
|
||||
case token[0]
|
||||
def validate_script
|
||||
commands.each do |command, *args|
|
||||
case command
|
||||
when :create_alias
|
||||
tag_alias = TagAlias.new(creator: User.system, forum_topic_id: forum_topic_id, status: "pending", antecedent_name: token[1], consequent_name: token[2], skip_secondary_validations: skip_secondary_validations)
|
||||
unless tag_alias.valid?
|
||||
raise Error, "Error: #{tag_alias.errors.full_messages.join("; ")} (create alias #{tag_alias.antecedent_name} -> #{tag_alias.consequent_name})"
|
||||
tag_alias = TagAlias.new(creator: User.system, antecedent_name: args[0], consequent_name: args[1], skip_secondary_validations: skip_secondary_validations)
|
||||
if tag_alias.invalid?
|
||||
errors[:base] << "Can't create alias #{tag_alias.antecedent_name} -> #{tag_alias.consequent_name} (#{tag_alias.errors.full_messages.join("; ")})"
|
||||
end
|
||||
|
||||
when :create_implication
|
||||
tag_implication = TagImplication.new(creator: User.system, forum_topic_id: forum_topic_id, status: "pending", antecedent_name: token[1], consequent_name: token[2], skip_secondary_validations: skip_secondary_validations)
|
||||
unless tag_implication.valid?
|
||||
raise Error, "Error: #{tag_implication.errors.full_messages.join("; ")} (create implication #{tag_implication.antecedent_name} -> #{tag_implication.consequent_name})"
|
||||
tag_implication = TagImplication.new(creator: User.system, antecedent_name: args[0], consequent_name: args[1], skip_secondary_validations: skip_secondary_validations)
|
||||
if tag_implication.invalid?
|
||||
errors[:base] << "Can't create implication #{tag_implication.antecedent_name} -> #{tag_implication.consequent_name} (#{tag_implication.errors.full_messages.join("; ")})"
|
||||
end
|
||||
|
||||
when :remove_alias, :remove_implication, :mass_update, :change_category
|
||||
when :remove_alias
|
||||
tag_alias = TagAlias.active.find_by(antecedent_name: args[0], consequent_name: args[1])
|
||||
if tag_alias.nil?
|
||||
errors[:base] << "Can't remove alias #{args[0]} -> #{args[1]} (alias doesn't exist)"
|
||||
end
|
||||
|
||||
when :remove_implication
|
||||
tag_implication = TagImplication.active.find_by(antecedent_name: args[0], consequent_name: args[1])
|
||||
if tag_implication.nil?
|
||||
errors[:base] << "Can't remove implication #{args[0]} -> #{args[1]} (implication doesn't exist)"
|
||||
end
|
||||
|
||||
when :change_category
|
||||
tag = Tag.find_by_name(args[0])
|
||||
if tag.nil?
|
||||
errors[:base] << "Can't change category #{args[0]} -> #{args[1]} (the '#{args[0]}' tag doesn't exist)"
|
||||
end
|
||||
|
||||
when :mass_update
|
||||
# okay
|
||||
|
||||
when :invalid_line
|
||||
errors[:base] << "Invalid line: #{args[0]}"
|
||||
|
||||
else
|
||||
raise NotImplementedError, "Unknown token: #{token[0]}" # should never happen
|
||||
# should never happen
|
||||
raise Error, "Unknown command: #{command}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def process!(approver)
|
||||
ActiveRecord::Base.transaction do
|
||||
tokens.map do |token|
|
||||
case token[0]
|
||||
validate!
|
||||
|
||||
commands.map do |command, *args|
|
||||
case command
|
||||
when :create_alias
|
||||
tag_alias = TagAlias.create(creator: approver, forum_topic_id: forum_topic_id, status: "pending", antecedent_name: token[1], consequent_name: token[2], skip_secondary_validations: skip_secondary_validations)
|
||||
unless tag_alias.valid?
|
||||
raise Error, "Error: #{tag_alias.errors.full_messages.join("; ")} (create alias #{tag_alias.antecedent_name} -> #{tag_alias.consequent_name})"
|
||||
end
|
||||
tag_alias = TagAlias.create!(creator: approver, forum_topic_id: forum_topic_id, status: "pending", antecedent_name: args[0], consequent_name: args[1], skip_secondary_validations: skip_secondary_validations)
|
||||
tag_alias.approve!(approver: approver)
|
||||
|
||||
when :create_implication
|
||||
tag_implication = TagImplication.create(creator: approver, forum_topic_id: forum_topic_id, status: "pending", antecedent_name: token[1], consequent_name: token[2], skip_secondary_validations: skip_secondary_validations)
|
||||
unless tag_implication.valid?
|
||||
raise Error, "Error: #{tag_implication.errors.full_messages.join("; ")} (create implication #{tag_implication.antecedent_name} -> #{tag_implication.consequent_name})"
|
||||
end
|
||||
tag_implication = TagImplication.create!(creator: approver, forum_topic_id: forum_topic_id, status: "pending", antecedent_name: args[0], consequent_name: args[1], skip_secondary_validations: skip_secondary_validations)
|
||||
tag_implication.approve!(approver: approver)
|
||||
|
||||
when :remove_alias
|
||||
tag_alias = TagAlias.active.find_by(antecedent_name: token[1], consequent_name: token[2])
|
||||
raise Error, "Alias for #{token[1]} not found" if tag_alias.nil?
|
||||
tag_alias = TagAlias.active.find_by!(antecedent_name: args[0], consequent_name: args[1])
|
||||
tag_alias.reject!
|
||||
|
||||
when :remove_implication
|
||||
tag_implication = TagImplication.active.find_by(antecedent_name: token[1], consequent_name: token[2])
|
||||
raise Error, "Implication for #{token[1]} not found" if tag_implication.nil?
|
||||
tag_implication = TagImplication.active.find_by!(antecedent_name: args[0], consequent_name: args[1])
|
||||
tag_implication.reject!
|
||||
|
||||
when :mass_update
|
||||
TagBatchChangeJob.perform_later(token[1], token[2], User.system, "127.0.0.1")
|
||||
TagBatchChangeJob.perform_later(args[0], args[1], User.system, "127.0.0.1")
|
||||
|
||||
when :change_category
|
||||
tag = Tag.find_or_create_by_name(token[1])
|
||||
tag.category = Tag.categories.value_for(token[2])
|
||||
tag.save
|
||||
tag = Tag.find_or_create_by_name(args[0])
|
||||
tag.update!(category: Tag.categories.value_for(args[1]))
|
||||
|
||||
else
|
||||
raise Error, "Unknown token: #{token[0]}"
|
||||
# should never happen
|
||||
raise Error, "Unknown command: #{command}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def affected_tags
|
||||
tokens.flat_map do |type, *args|
|
||||
case type
|
||||
commands.flat_map do |command, *args|
|
||||
case command
|
||||
when :create_alias, :remove_alias, :create_implication, :remove_implication
|
||||
[args[0], args[1]]
|
||||
when :mass_update
|
||||
@@ -113,13 +131,11 @@ class BulkUpdateRequestProcessor
|
||||
args[0]
|
||||
end
|
||||
end.sort.uniq
|
||||
rescue Error
|
||||
[]
|
||||
end
|
||||
|
||||
def is_tag_move_allowed?
|
||||
tokens.all? do |type, *args|
|
||||
case type
|
||||
commands.all? do |command, *args|
|
||||
case command
|
||||
when :create_alias
|
||||
BulkUpdateRequestProcessor.is_tag_move_allowed?(args[0], args[1])
|
||||
when :mass_update
|
||||
@@ -134,16 +150,17 @@ class BulkUpdateRequestProcessor
|
||||
end
|
||||
|
||||
def to_dtext
|
||||
tokens.map do |token|
|
||||
case token[0]
|
||||
commands.map do |command, *args|
|
||||
case command
|
||||
when :create_alias, :create_implication, :remove_alias, :remove_implication
|
||||
"#{token[0].to_s.tr("_", " ")} [[#{token[1]}]] -> [[#{token[2]}]]"
|
||||
"#{command.to_s.tr("_", " ")} [[#{args[0]}]] -> [[#{args[1]}]]"
|
||||
when :mass_update
|
||||
"mass update {{#{token[1]}}} -> #{token[2]}"
|
||||
"mass update {{#{args[0]}}} -> #{args[1]}"
|
||||
when :change_category
|
||||
"category [[#{token[1]}]] -> #{token[2]}"
|
||||
"category [[#{args[0]}]] -> #{args[1]}"
|
||||
else
|
||||
raise "Unknown token: #{token[0]}"
|
||||
# should never happen
|
||||
raise Error, "Unknown command: #{command}"
|
||||
end
|
||||
end.join("\n")
|
||||
end
|
||||
@@ -155,6 +172,4 @@ class BulkUpdateRequestProcessor
|
||||
(antecedent_tag.blank? || antecedent_tag.empty? || (antecedent_tag.artist? && antecedent_tag.post_count <= 100)) &&
|
||||
(consequent_tag.blank? || consequent_tag.empty? || (consequent_tag.artist? && consequent_tag.post_count <= 100))
|
||||
end
|
||||
|
||||
memoize :tokens
|
||||
end
|
||||
|
||||
@@ -9,14 +9,14 @@ class BulkUpdateRequest < ApplicationRecord
|
||||
belongs_to :approver, optional: true, class_name: "User"
|
||||
|
||||
before_validation :normalize_text
|
||||
before_validation :update_tags
|
||||
validates_presence_of :reason, on: :create
|
||||
validates_presence_of :script
|
||||
validates_presence_of :title, if: ->(rec) {rec.forum_topic_id.blank?}
|
||||
validates_presence_of :forum_topic, if: ->(rec) {rec.forum_topic_id.present?}
|
||||
validates_inclusion_of :status, :in => %w(pending approved rejected)
|
||||
validate :validate_script, :on => :create
|
||||
validate :validate_script, if: :script_changed?
|
||||
|
||||
before_save :update_tags, if: :script_changed?
|
||||
after_create :create_forum_topic
|
||||
|
||||
scope :pending_first, -> { order(Arel.sql("(case status when 'pending' then 0 when 'approved' then 1 else 2 end)")) }
|
||||
@@ -108,17 +108,14 @@ class BulkUpdateRequest < ApplicationRecord
|
||||
end
|
||||
end
|
||||
|
||||
module ValidationMethods
|
||||
def validate_script
|
||||
processor.validate!
|
||||
rescue BulkUpdateRequestProcessor::Error => e
|
||||
errors[:base] << e.message
|
||||
def validate_script
|
||||
if processor.invalid?
|
||||
errors[:base] << processor.errors.full_messages.join("; ")
|
||||
end
|
||||
end
|
||||
|
||||
extend SearchMethods
|
||||
include ApprovalMethods
|
||||
include ValidationMethods
|
||||
|
||||
def normalize_text
|
||||
self.script = script.downcase
|
||||
@@ -133,7 +130,7 @@ class BulkUpdateRequest < ApplicationRecord
|
||||
end
|
||||
|
||||
def processor
|
||||
@processor ||= BulkUpdateRequestProcessor.new(script, forum_topic_id: forum_topic_id, skip_secondary_validations: skip_secondary_validations)
|
||||
@processor ||= BulkUpdateRequestProcessor.new(self)
|
||||
end
|
||||
|
||||
def is_tag_move_allowed?
|
||||
|
||||
@@ -7,7 +7,7 @@ class BulkUpdateRequestsControllerTest < ActionDispatch::IntegrationTest
|
||||
@builder = create(:builder_user)
|
||||
@admin = create(:admin_user)
|
||||
as(@admin) { @forum_topic = create(:forum_topic, id: 100, category_id: 0) }
|
||||
as(@user) { @bulk_update_request = create(:bulk_update_request, user: @user, forum_topic: @forum_topic) }
|
||||
as(@user) { @bulk_update_request = create(:bulk_update_request, user: @user, forum_topic: @forum_topic, script: "create alias aaa -> bbb") }
|
||||
end
|
||||
|
||||
context "#new" do
|
||||
@@ -71,6 +71,12 @@ class BulkUpdateRequestsControllerTest < ActionDispatch::IntegrationTest
|
||||
assert_response 403
|
||||
assert_equal("create alias aaa -> bbb", @bulk_update_request.reload.script)
|
||||
end
|
||||
|
||||
should "fail for an invalid script" do
|
||||
put_auth bulk_update_request_path(@bulk_update_request.id), @user, params: { bulk_update_request: { script: "create alis gray -> grey" }}
|
||||
assert_response :success
|
||||
assert_equal("create alias aaa -> bbb", @bulk_update_request.reload.script)
|
||||
end
|
||||
end
|
||||
|
||||
context "#index" do
|
||||
|
||||
@@ -1,128 +0,0 @@
|
||||
require 'test_helper'
|
||||
|
||||
class BulkUpdateRequestProcessorTest < ActiveSupport::TestCase
|
||||
context "The bulk update request processor" do
|
||||
setup do
|
||||
CurrentUser.user = create(:admin_user)
|
||||
CurrentUser.ip_addr = "127.0.0.1"
|
||||
end
|
||||
|
||||
teardown do
|
||||
CurrentUser.user = nil
|
||||
CurrentUser.ip_addr = nil
|
||||
end
|
||||
|
||||
context "category command" do
|
||||
should "change the tag's category" do
|
||||
@tag = create(:tag, name: "hello")
|
||||
@script = "category hello -> artist\n"
|
||||
@processor = BulkUpdateRequestProcessor.new(@script)
|
||||
@processor.process!(CurrentUser.user)
|
||||
|
||||
assert_equal(Tag.categories.value_for("artist"), @tag.reload.category)
|
||||
end
|
||||
end
|
||||
|
||||
context "#affected_tags" do
|
||||
setup do
|
||||
create(:post, tag_string: "aaa")
|
||||
create(:post, tag_string: "bbb")
|
||||
create(:post, tag_string: "ccc")
|
||||
create(:post, tag_string: "ddd")
|
||||
create(:post, tag_string: "eee")
|
||||
|
||||
@script = "create alias aaa -> 000\n" +
|
||||
"create implication bbb -> 111\n" +
|
||||
"remove alias ccc -> 222\n" +
|
||||
"remove implication ddd -> 333\n" +
|
||||
"mass update eee -> 444\n"
|
||||
end
|
||||
|
||||
should "return the correct tags" do
|
||||
@processor = BulkUpdateRequestProcessor.new(@script)
|
||||
assert_equal(%w(000 111 222 333 444 aaa bbb ccc ddd eee), @processor.affected_tags)
|
||||
end
|
||||
end
|
||||
|
||||
context "given a valid list" do
|
||||
should "process it" do
|
||||
@list = "create alias abc -> def\ncreate implication aaa -> bbb\n"
|
||||
@processor = BulkUpdateRequestProcessor.new(@list)
|
||||
@processor.process!(CurrentUser.user)
|
||||
|
||||
assert(TagAlias.exists?(antecedent_name: "abc", consequent_name: "def"))
|
||||
assert(TagImplication.exists?(antecedent_name: "aaa", consequent_name: "bbb"))
|
||||
end
|
||||
end
|
||||
|
||||
context "given a list with an invalid command" do
|
||||
should "throw an exception" do
|
||||
@list = "zzzz abc -> def\n"
|
||||
@processor = BulkUpdateRequestProcessor.new(@list)
|
||||
|
||||
assert_raises(BulkUpdateRequestProcessor::Error) do
|
||||
@processor.process!(CurrentUser.user)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "given a list with a logic error" do
|
||||
should "throw an exception" do
|
||||
@list = "remove alias zzz -> yyy\n"
|
||||
@processor = BulkUpdateRequestProcessor.new(@list)
|
||||
|
||||
assert_raises(BulkUpdateRequestProcessor::Error) do
|
||||
@processor.process!(CurrentUser.user)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
should "rename an aliased tag's artist entry and wiki page" do
|
||||
tag1 = create(:tag, name: "aaa", category: 1)
|
||||
tag2 = create(:tag, name: "bbb")
|
||||
wiki = create(:wiki_page, title: "aaa")
|
||||
artist = create(:artist, name: "aaa")
|
||||
|
||||
@processor = BulkUpdateRequestProcessor.new("create alias aaa -> bbb")
|
||||
@processor.process!(CurrentUser.user)
|
||||
perform_enqueued_jobs
|
||||
|
||||
assert_equal("bbb", artist.reload.name)
|
||||
assert_equal("bbb", wiki.reload.title)
|
||||
end
|
||||
|
||||
context "remove alias and remove implication commands" do
|
||||
setup do
|
||||
@ta = create(:tag_alias, antecedent_name: "a", consequent_name: "b", status: "active")
|
||||
@ti = create(:tag_implication, antecedent_name: "c", consequent_name: "d", status: "active")
|
||||
@script = %{
|
||||
remove alias a -> b
|
||||
remove implication c -> d
|
||||
}
|
||||
@processor = BulkUpdateRequestProcessor.new(@script)
|
||||
end
|
||||
|
||||
should "set aliases and implications as deleted" do
|
||||
@processor.process!(CurrentUser.user)
|
||||
|
||||
assert_equal("deleted", @ta.reload.status)
|
||||
assert_equal("deleted", @ti.reload.status)
|
||||
end
|
||||
|
||||
should "create modactions for each removal" do
|
||||
assert_difference(-> { ModAction.count }, 2) do
|
||||
@processor.process!(CurrentUser.user)
|
||||
end
|
||||
end
|
||||
|
||||
should "only remove active aliases and implications" do
|
||||
@ta2 = create(:tag_alias, antecedent_name: "a", consequent_name: "b", status: "pending")
|
||||
@ti2 = create(:tag_implication, antecedent_name: "c", consequent_name: "d", status: "pending")
|
||||
|
||||
@processor.process!(CurrentUser.user)
|
||||
assert_equal("pending", @ta2.reload.status)
|
||||
assert_equal("pending", @ti2.reload.status)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -13,24 +13,209 @@ class BulkUpdateRequestTest < ActiveSupport::TestCase
|
||||
CurrentUser.ip_addr = nil
|
||||
end
|
||||
|
||||
should "parse the tags inside the script" do
|
||||
@bur = create(:bulk_update_request, script: %{
|
||||
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
|
||||
})
|
||||
|
||||
assert_equal(%w[000 111 222 333 444 aaa bbb ccc ddd eee iii], @bur.tags)
|
||||
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(:bulk_update_request, script: "category hello -> artist")
|
||||
@bur.approve!(@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(:bulk_update_request, script: "create alias foo -> bar")
|
||||
|
||||
@bur.approve!(@admin)
|
||||
perform_enqueued_jobs
|
||||
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 "fail if the alias is invalid" do
|
||||
create(:tag_alias, antecedent_name: "aaa", consequent_name: "bbb")
|
||||
@bur = build(:bulk_update_request, script: "create alias bbb -> aaa")
|
||||
|
||||
assert_equal(false, @bur.valid?)
|
||||
assert_equal(["Can't create alias bbb -> aaa (A tag alias for aaa already exists)"], @bur.errors.full_messages)
|
||||
end
|
||||
end
|
||||
|
||||
context "the create implication command" do
|
||||
should "create an implication" do
|
||||
@bur = create(:bulk_update_request, script: "create implication foo -> bar")
|
||||
@bur.approve!(@admin)
|
||||
perform_enqueued_jobs
|
||||
|
||||
@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_eventually "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(:bulk_update_request, script: "remove alias foo -> bar")
|
||||
@bur.approve!(@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: "pending")
|
||||
@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(:bulk_update_request, script: "remove implication foo -> bar")
|
||||
@bur.approve!(@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: "pending")
|
||||
@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(:bulk_update_request, script: "mass update foo -> bar")
|
||||
@bur.approve!(@admin)
|
||||
perform_enqueued_jobs
|
||||
end
|
||||
|
||||
should "update the tags" do
|
||||
assert_equal("bar", @post.reload.tag_string)
|
||||
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
|
||||
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")
|
||||
@@ -81,67 +266,6 @@ class BulkUpdateRequestTest < ActiveSupport::TestCase
|
||||
assert_match(/zzz/, bur.forum_post.body)
|
||||
end
|
||||
|
||||
context "that has an invalid alias" do
|
||||
setup do
|
||||
@alias1 = create(:tag_alias, antecedent_name: "aaa", consequent_name: "bbb", creator: @admin)
|
||||
@req = FactoryBot.build(:bulk_update_request, :script => "create alias bbb -> aaa")
|
||||
end
|
||||
|
||||
should "not validate" do
|
||||
assert_difference("TagAlias.count", 0) do
|
||||
@req.save
|
||||
end
|
||||
assert_equal(["Error: A tag alias for aaa already exists (create alias bbb -> aaa)"], @req.errors.full_messages)
|
||||
end
|
||||
end
|
||||
|
||||
context "for an implication that is redundant with an existing implication" do
|
||||
should "not validate" do
|
||||
FactoryBot.create(:tag_implication, :antecedent_name => "a", :consequent_name => "b")
|
||||
FactoryBot.create(:tag_implication, :antecedent_name => "b", :consequent_name => "c")
|
||||
bur = FactoryBot.build(:bulk_update_request, :script => "imply a -> c")
|
||||
bur.save
|
||||
|
||||
assert_equal(["Error: a already implies c through another implication (create implication a -> c)"], bur.errors.full_messages)
|
||||
end
|
||||
end
|
||||
|
||||
context "for an implication that is redundant with another implication in the same BUR" do
|
||||
setup do
|
||||
FactoryBot.create(:tag_implication, :antecedent_name => "b", :consequent_name => "c")
|
||||
@bur = FactoryBot.build(:bulk_update_request, :script => "imply a -> b\nimply a -> c")
|
||||
@bur.save
|
||||
end
|
||||
|
||||
should "not process" do
|
||||
assert_no_difference("TagImplication.count") do
|
||||
@bur.approve!(@admin)
|
||||
end
|
||||
end
|
||||
|
||||
should_eventually "not validate" do
|
||||
assert_equal(["Error: a already implies c through another implication (create implication a -> c)"], @bur.errors.full_messages)
|
||||
end
|
||||
end
|
||||
|
||||
context "for a `category <tag> -> type` change" do
|
||||
should "work" do
|
||||
tag = Tag.find_or_create_by_name("tagme")
|
||||
bur = FactoryBot.create(:bulk_update_request, :script => "category tagme -> meta")
|
||||
bur.approve!(@admin)
|
||||
|
||||
assert_equal(Tag.categories.meta, tag.reload.category)
|
||||
end
|
||||
|
||||
should "work for a new tag" do
|
||||
bur = FactoryBot.create(:bulk_update_request, :script => "category new_tag -> meta")
|
||||
bur.approve!(@admin)
|
||||
|
||||
assert_not_nil(Tag.find_by_name("new_tag"))
|
||||
assert_equal(Tag.categories.meta, Tag.find_by_name("new_tag").category)
|
||||
end
|
||||
end
|
||||
|
||||
context "with an associated forum topic" do
|
||||
setup do
|
||||
@topic = create(:forum_topic, title: "[bulk] hoge", creator: @admin)
|
||||
|
||||
Reference in New Issue
Block a user