From 52f5b33484d4c1157123ea3b058ff59d21e4cf74 Mon Sep 17 00:00:00 2001 From: Ryan Lue Date: Wed, 3 Jul 2019 18:02:29 +0200 Subject: [PATCH] Refactoring: Migrate tag_test to RSpec --- app/models/tag.rb | 18 +- spec/factories/tag/item.rb | 5 + spec/models/concerns/has_tags_examples.rb | 66 ++++ spec/models/tag/item_spec.rb | 125 +++++++ spec/models/tag_spec.rb | 199 +++++++---- spec/models/ticket_spec.rb | 2 + test/unit/tag_test.rb | 388 ---------------------- 7 files changed, 335 insertions(+), 468 deletions(-) create mode 100644 spec/factories/tag/item.rb create mode 100644 spec/models/concerns/has_tags_examples.rb create mode 100644 spec/models/tag/item_spec.rb delete mode 100644 test/unit/tag_test.rb diff --git a/app/models/tag.rb b/app/models/tag.rb index 9eae95c3f..768efde0d 100644 --- a/app/models/tag.rb +++ b/app/models/tag.rb @@ -145,20 +145,10 @@ returns =end def self.tag_list(data) - tag_object_id_requested = Tag::Object.lookup(name: data[:object]) - return [] if !tag_object_id_requested - - tag_search = Tag.where( - tag_object_id: tag_object_id_requested, - o_id: data[:o_id], - ).order(:id) - - tag_search.each_with_object([]) do |tag, result| - tag_item = Tag::Item.lookup(id: tag.tag_item_id) - next if !tag_item - - result.push tag_item.name - end + Tag.joins(:tag_item, :tag_object) + .where(o_id: data[:o_id], tag_objects: { name: data[:object] }) + .order(:id) + .pluck('tag_items.name') end class Object < ApplicationModel diff --git a/spec/factories/tag/item.rb b/spec/factories/tag/item.rb new file mode 100644 index 000000000..ce8cb9fc5 --- /dev/null +++ b/spec/factories/tag/item.rb @@ -0,0 +1,5 @@ +FactoryBot.define do + factory :'tag/item', aliases: %i[tag_item] do + sequence(:name) { |n| "Item #{n}" } + end +end diff --git a/spec/models/concerns/has_tags_examples.rb b/spec/models/concerns/has_tags_examples.rb new file mode 100644 index 000000000..edc87a3d8 --- /dev/null +++ b/spec/models/concerns/has_tags_examples.rb @@ -0,0 +1,66 @@ +RSpec.shared_examples 'HasTags' do + subject { create(described_class.name.underscore) } + + describe '#tag_add' do + let(:item_name) { 'foo' } + + it 'delegates to Tag.tag_add' do + expect(Tag) + .to receive(:tag_add) + .with(object: described_class.name, + o_id: subject.id, + item: item_name, + created_by_id: nil) + + subject.tag_add(item_name) + end + + it 'optionally accepts a current_user_id argument' do + expect(Tag) + .to receive(:tag_add) + .with(object: described_class.name, + o_id: subject.id, + item: item_name, + created_by_id: 1) + + subject.tag_add(item_name, 1) + end + end + + describe '#tag_remove' do + let(:item_name) { 'foo' } + + it 'delegates to Tag.tag_remove' do + expect(Tag) + .to receive(:tag_remove) + .with(object: described_class.name, + o_id: subject.id, + item: item_name, + created_by_id: nil) + + subject.tag_remove(item_name) + end + + it 'optionally accepts a current_user_id argument' do + expect(Tag) + .to receive(:tag_remove) + .with(object: described_class.name, + o_id: subject.id, + item: item_name, + created_by_id: 1) + + subject.tag_remove(item_name, 1) + end + end + + describe '#tag_list' do + it 'delegates to Tag.tag_list' do + expect(Tag) + .to receive(:tag_list) + .with(object: described_class.name, + o_id: subject.id) + + subject.tag_list + end + end +end diff --git a/spec/models/tag/item_spec.rb b/spec/models/tag/item_spec.rb new file mode 100644 index 000000000..ea1884945 --- /dev/null +++ b/spec/models/tag/item_spec.rb @@ -0,0 +1,125 @@ +require 'rails_helper' + +RSpec.describe Tag::Item do + subject(:item) { create(:'tag/item') } + + describe '.rename' do + context 'when given a unique item name' do + it 'updates the name on the Tag::Item' do + expect { described_class.rename(id: item.id, name: 'foo') } + .to change { item.reload.name }.to('foo') + end + + it 'strips trailing/leading whitespace' do + expect { described_class.rename(id: item.id, name: ' foo ') } + .to change { item.reload.name }.to('foo') + end + end + + context 'when given a conflicting/existing item name' do + let!(:item_2) { create(:'tag/item', name: 'foo') } + + context 'with no redundant tags' do + let!(:tag) { create(:tag, o: Ticket.first, tag_item: item) } + + it 'reassigns all tags from old-name item to new-name item' do + expect { described_class.rename(id: item.id, name: item_2.name) } + .to change { tag.reload.tag_item }.to(item_2) + .and change { Ticket.first.tag_list }.to([item_2.name]) + end + + it 'strips trailing/leading whitespace' do + expect { described_class.rename(id: item.id, name: " #{item_2.name} ") } + .to change { tag.reload.tag_item }.to(item_2) + .and change { Ticket.first.tag_list }.to([item_2.name]) + end + end + + context 'with redundant tags' do + let!(:tags) do + [create(:tag, o: Ticket.first, tag_item: item), + create(:tag, o: Ticket.first, tag_item: item_2)] + end + + it 'removes the tag assigned to old-name item' do + expect { described_class.rename(id: item.id, name: item_2.name) } + .to change { Tag.exists?(id: tags.first.id) }.to(false) + .and change { Ticket.first.tag_list }.to([item_2.name]) + end + + it 'strips trailing/leading whitespace' do + expect { described_class.rename(id: item.id, name: " #{item_2.name} ") } + .to change { Tag.exists?(id: tags.first.id) }.to(false) + .and change { Ticket.first.tag_list }.to([item_2.name]) + end + end + + it 'deletes the original item' do + expect { described_class.rename(id: item.id, name: item_2.name) } + .to change { described_class.exists?(name: item.name) }.to(false) + end + end + + shared_examples 'updating references to tag names' do |object_klass:, method:, label: 'ticket.tags'| + subject(:item) { create(:'tag/item', name: 'test1') } + + context "with reference to renamed tag in its #{method} hash (contains-one)" do + let(:object) { create(object_klass.name.underscore, method => { label => tag_matcher }) } + let(:tag_matcher) { { operator: 'contains one', value: 'test1' } } + + it 'updates reference with new tag name' do + expect { described_class.rename(id: item.id, name: 'test1_renamed') } + .to change { object.reload.send(method)[label][:value] } + .from('test1').to('test1_renamed') + end + end + + context "with reference to renamed tag in its #{method} hash (contains-all)" do + let(:object) { create(object_klass.name.underscore, method => { label => tag_matcher }) } + let(:tag_matcher) { { operator: 'contains all', value: 'test1, test2, test3' } } + + it 'updates reference with new tag name' do + expect { described_class.rename(id: item.id, name: 'test1_renamed') } + .to change { object.reload.send(method)[label][:value] } + .from('test1, test2, test3').to('test1_renamed, test2, test3') + end + end + end + + context 'for Overview object' do + include_examples 'updating references to tag names', object_klass: Overview, method: :condition + end + + context 'for Trigger object' do + include_examples 'updating references to tag names', object_klass: Trigger, method: :condition + include_examples 'updating references to tag names', object_klass: Trigger, method: :perform + end + + context 'for scheduler (Job) object' do + include_examples 'updating references to tag names', object_klass: Job, method: :condition + include_examples 'updating references to tag names', object_klass: Job, method: :perform + end + + context 'for PostmasterFilter object' do + include_examples 'updating references to tag names', object_klass: PostmasterFilter, method: :perform, label: 'x-zammad-ticket-tags' + end + end + + describe '.remove' do + let!(:tags) do + [create(:tag, tag_item: item, o: User.first), + create(:tag, tag_item: item, o: Ticket.first)] + end + + it 'removes the specified Tag::Item' do + expect { described_class.remove(item.id) } + .to change { described_class.exists?(id: item.id) }.to(false) + end + + it 'removes all associated Tags' do + expect { described_class.remove(item.id) } + .to change { Tag.exists?(id: tags.first.id) }.to(false) + .and change { Tag.exists?(id: tags.second.id) }.to(false) + end + end +end diff --git a/spec/models/tag_spec.rb b/spec/models/tag_spec.rb index a59048c8d..c960c9142 100644 --- a/spec/models/tag_spec.rb +++ b/spec/models/tag_spec.rb @@ -1,90 +1,157 @@ require 'rails_helper' -RSpec.describe Tag do +RSpec.describe Tag, type: :model do + subject(:tag) { create(:tag) } - context 'rename' do - before do - Tag::Item.lookup_by_name_and_create('test1') + describe '.tag_add' do + it 'touches the target object' do + expect { described_class.tag_add(object: 'Ticket', item: 'foo', o_id: Ticket.first.id, created_by_id: 1) } + .to change { Ticket.first.updated_at } end - def tag_rename - Tag::Item.rename( - id: Tag::Item.lookup(name: 'test1').id, - name: 'test1_renamed', - updated_by_id: 1, - ) + context 'when a Tag::Object does not exist for the given class' do + it 'creates it and assigns it to a new Tag' do + expect { described_class.tag_add(object: 'Foo', item: 'bar', o_id: 1, created_by_id: 1) } + .to change(Tag, :count).by(1) + .and change { Tag::Object.exists?(name: 'Foo') }.to(true) + .and change { Tag.last&.tag_object&.name }.to('Foo') + end end - it 'overview conditions with a single tag' do - object = create :overview, condition: { 'ticket.tags' => { operator: 'contains one', value: 'test1' } } - tag_rename - expect(Overview.find(object.id).condition['ticket.tags'][:value]).to eq('test1_renamed') + context 'when a Tag::Object already exists for the given class' do + let!(:tag_object) { Tag::Object.find_or_create_by(name: 'Ticket') } + + it 'assigns it to a new Tag' do + expect { described_class.tag_add(object: 'Ticket', item: 'foo', o_id: 1, created_by_id: 1) } + .to change(Tag, :count).by(1) + .and not_change(Tag::Object, :count) + .and change { Tag.last&.tag_object&.name }.to('Ticket') + end end - it 'overview conditions with a tag list ' do - object = create :overview, condition: { 'ticket.tags' => { operator: 'contains all', value: 'test1, test2, test3' } } - tag_rename - expect(Overview.find(object.id).condition['ticket.tags'][:value]).to eq('test1_renamed, test2, test3') + context 'when a Tag::Item does not exist with the given name' do + it 'creates it and assigns it to a new Tag' do + expect { described_class.tag_add(object: 'Ticket', item: 'foo', o_id: 1, created_by_id: 1) } + .to change(Tag, :count).by(1) + .and change { Tag::Item.exists?(name: 'foo') }.to(true) + .and change { Tag.last&.tag_item&.name }.to('foo') + end + + it 'strips trailing/leading whitespace' do + expect { described_class.tag_add(object: 'Ticket', item: ' foo ', o_id: 1, created_by_id: 1) } + .to change(Tag, :count).by(1) + .and change { Tag::Item.exists?(name: 'foo') }.to(true) + .and change { Tag.last&.tag_item&.name }.to('foo') + end + + context 'and the name contains 8-bit Unicode characters' do + it 'creates it and assigns it to a new Tag' do + expect { described_class.tag_add(object: 'Ticket', item: 'fooöäüß', o_id: 1, created_by_id: 1) } + .to change(Tag, :count).by(1) + .and change { Tag::Item.exists?(name: 'fooöäüß') }.to(true) + .and change { Tag.last&.tag_item&.name }.to('fooöäüß') + end + end + + context 'but the name is a case-sensitive variant of an existing Tag::Item' do + let!(:tag_item) { create(:'tag/item', name: 'foo') } + + it 'creates it and assigns it to a new Tag' do + expect { described_class.tag_add(object: 'Ticket', item: 'FOO', o_id: 1, created_by_id: 1) } + .to change(Tag, :count).by(1) + .and change { Tag::Item.pluck(:name).include?('FOO') }.to(true) # .exists?(name: 'FOO') fails on MySQL + .and change { Tag.last&.tag_item&.name }.to('FOO') + end + end end - it 'trigger conditions with a single tag' do - object = create :trigger, condition: { 'ticket.tags' => { operator: 'contains one', value: 'test1' } } - tag_rename - expect(Trigger.find(object.id).condition['ticket.tags'][:value]).to eq('test1_renamed') + context 'when a Tag::Item already exists with the given name' do + let!(:tag_item) { create(:'tag/item', name: 'foo') } + + it 'assigns it to a new Tag' do + expect { described_class.tag_add(object: 'Ticket', item: 'foo', o_id: 1, created_by_id: 1) } + .to change(Tag, :count).by(1) + .and not_change(Tag::Item, :count) + .and change { Tag.last&.tag_item&.name }.to('foo') + end + + it 'strips leading/trailing whitespace' do + expect { described_class.tag_add(object: 'Ticket', item: ' foo ', o_id: 1, created_by_id: 1) } + .to change(Tag, :count).by(1) + .and not_change(Tag::Item, :count) + .and change { Tag.last&.tag_item&.name }.to('foo') + end end - it 'trigger conditions with a tag list ' do - object = create :trigger, condition: { 'ticket.tags' => { operator: 'contains all', value: 'test1, test2, test3' } } - tag_rename - expect(Trigger.find(object.id).condition['ticket.tags'][:value]).to eq('test1_renamed, test2, test3') + context 'when a Tag already exists for the specified record with the given name' do + let!(:tag) { create(:tag, o: Ticket.first, tag_item: tag_item) } + let(:tag_item) { create(:'tag/item', name: 'foo') } + + it 'does not create any records' do + expect { described_class.tag_add(object: 'Ticket', item: 'foo', o_id: Ticket.first.id, created_by_id: 1) } + .to not_change(Tag, :count) + .and not_change(Tag::Item, :count) + end + end + end + + describe '.tag_remove' do + it 'touches the target object' do + expect { described_class.tag_remove(object: 'Ticket', item: 'foo', o_id: Ticket.first.id, created_by_id: 1) } + .to change { Ticket.first.updated_at } end - it 'trigger performs with a single tag' do - object = create :trigger, perform: { 'ticket.tags' => { operator: 'contains one', value: 'test1' } } - tag_rename - expect(Trigger.find(object.id).perform['ticket.tags'][:value]).to eq('test1_renamed') + context 'when a matching Tag exists' do + let!(:tag) { create(:tag, o: Ticket.first, tag_item: tag_item) } + let(:tag_item) { create(:'tag/item', name: 'foo') } + + it 'destroys the Tag' do + expect { described_class.tag_remove(object: 'Ticket', o_id: Ticket.first.id, item: 'foo') } + .to change(Tag, :count).by(-1) + end end - it 'trigger performs with a tag list ' do - object = create :trigger, perform: { 'ticket.tags' => { operator: 'contains all', value: 'test1, test2, test3' } } - tag_rename - expect(Trigger.find(object.id).perform['ticket.tags'][:value]).to eq('test1_renamed, test2, test3') + context 'when no matching Tag exists' do + it 'makes no changes' do + expect { described_class.tag_remove(object: 'Ticket', o_id: Ticket.first.id, item: 'foo') } + .not_to change(Tag, :count) + end + end + end + + describe '.tag_list' do + context 'with ASCII item names' do + before { items.map { |i| create(:tag, tag_item: i, o: Ticket.first) } } + + let(:items) do + [ + create(:'tag/item', name: 'foo'), + create(:'tag/item', name: 'bar'), + create(:'tag/item', name: 'BAR'), + ] + end + + it 'returns all tag names (case-sensitive) for a given record' do + expect(described_class.tag_list(object: 'Ticket', o_id: Ticket.first.id)) + .to match_array(%w[foo bar BAR]) + end end - it 'scheduler conditions with a single tag' do - object = create :job, condition: { 'ticket.tags' => { operator: 'contains one', value: 'test1' } } - tag_rename - expect(Job.find(object.id).condition['ticket.tags'][:value]).to eq('test1_renamed') - end + context 'with Unicode (8-bit) item names' do + before { items.map { |i| create(:tag, tag_item: i, o: Ticket.first) } } - it 'scheduler conditions with a tag list ' do - object = create :job, condition: { 'ticket.tags' => { operator: 'contains all', value: 'test1, test2, test3' } } - tag_rename - expect(Job.find(object.id).condition['ticket.tags'][:value]).to eq('test1_renamed, test2, test3') - end + let(:items) do + [ + create(:'tag/item', name: 'fooöäüß'), + create(:'tag/item', name: 'baröäüß'), + create(:'tag/item', name: 'BARöäüß'), + ] + end - it 'scheduler performs with a single tag' do - object = create :job, perform: { 'ticket.tags' => { operator: 'contains one', value: 'test1' } } - tag_rename - expect(Job.find(object.id).perform['ticket.tags'][:value]).to eq('test1_renamed') - end - - it 'scheduler performs with a tag list ' do - object = create :job, perform: { 'ticket.tags' => { operator: 'contains all', value: 'test1, test2, test3' } } - tag_rename - expect(Job.find(object.id).perform['ticket.tags'][:value]).to eq('test1_renamed, test2, test3') - end - - it 'PostmasterFilter performs with a single tag' do - object = create :postmaster_filter, perform: { 'x-zammad-ticket-tags' => { operator: 'contains one', value: 'test1' } } - tag_rename - expect(PostmasterFilter.find(object.id).perform['x-zammad-ticket-tags'][:value]).to eq('test1_renamed') - end - - it 'PostmasterFilter performs with a tag list ' do - object = create :postmaster_filter, perform: { 'x-zammad-ticket-tags' => { operator: 'contains all', value: 'test1, test2, test3' } } - tag_rename - expect(PostmasterFilter.find(object.id).perform['x-zammad-ticket-tags'][:value]).to eq('test1_renamed, test2, test3') + it 'returns all tag names (case-sensitive) for a given record' do + expect(described_class.tag_list(object: 'Ticket', o_id: Ticket.first.id)) + .to match_array(%w[fooöäüß baröäüß BARöäüß]) + end end end end diff --git a/spec/models/ticket_spec.rb b/spec/models/ticket_spec.rb index 1f635e5bb..c4bf17126 100644 --- a/spec/models/ticket_spec.rb +++ b/spec/models/ticket_spec.rb @@ -3,6 +3,7 @@ require 'models/application_model_examples' require 'models/concerns/can_be_imported_examples' require 'models/concerns/can_lookup_examples' require 'models/concerns/has_history_examples' +require 'models/concerns/has_tags_examples' require 'models/concerns/has_xss_sanitized_note_examples' require 'models/concerns/has_object_manager_attributes_validation_examples' @@ -11,6 +12,7 @@ RSpec.describe Ticket, type: :model do it_behaves_like 'CanBeImported' it_behaves_like 'CanLookup' it_behaves_like 'HasHistory', history_relation_object: 'Ticket::Article' + it_behaves_like 'HasTags' it_behaves_like 'HasXssSanitizedNote', model_factory: :ticket it_behaves_like 'HasObjectManagerAttributesValidation' diff --git a/test/unit/tag_test.rb b/test/unit/tag_test.rb deleted file mode 100644 index 4949fd2e8..000000000 --- a/test/unit/tag_test.rb +++ /dev/null @@ -1,388 +0,0 @@ -require 'test_helper' - -class TagTest < ActiveSupport::TestCase - test 'tags' do - tests = [ - - # test 1 - { - tag_add: { - item: 'tag1', - object: 'Object1', - o_id: 123, - created_by_id: 1 - }, - verify: { - object: 'Object1', - items: { - 'tag1' => true, - 'tag2' => false, - }, - }, - }, - - # test 2 - { - tag_add: { - item: 'tag2', - object: 'Object1', - o_id: 123, - created_by_id: 1 - }, - verify: { - object: 'Object1', - items: { - 'tag1' => true, - 'tag2' => true, - }, - }, - }, - - { - tag_add: { - item: 'TAG2', - object: 'Object1', - o_id: 123, - created_by_id: 1 - }, - verify: { - object: 'Object1', - items: { - 'tag1' => true, - 'tag2' => true, - 'TAG2' => true, - }, - }, - }, - - # test 2 - { - tag_add: { - item: 'tagöäüß1', - object: 'Object2', - o_id: 123, - created_by_id: 1 - }, - verify: { - object: 'Object2', - items: { - 'tagöäüß1' => true, - 'tag2' => false, - }, - }, - }, - - # test 4 - { - tag_add: { - item: 'Tagöäüß2', - object: 'Object2', - o_id: 123, - created_by_id: 1 - }, - verify: { - object: 'Object2', - items: { - 'tagöäüß1' => true, - 'Tagöäüß2' => true, - 'tagöäüß3' => false, - }, - }, - }, - - # test 5 - { - tag_remove: { - item: 'tag1', - object: 'Object1', - o_id: 123, - created_by_id: 1 - }, - verify: { - object: 'Object1', - items: { - 'tag1' => false, - 'tag2' => true, - 'TAG2' => true, - }, - }, - }, - - # test 5 - { - tag_remove: { - item: 'TAG2', - object: 'Object1', - o_id: 123, - created_by_id: 1 - }, - verify: { - object: 'Object1', - items: { - 'tag1' => false, - 'tag2' => true, - 'TAG2' => false, - }, - }, - }, - - ] - tests.each do |test| - tags = nil - if test[:tag_add] - tags = test[:tag_add] - success = Tag.tag_add(tags) - assert(success, 'Tag.tag_add successful') - else - tags = test[:tag_remove] - success = Tag.tag_remove(tags) - assert(success, 'Tag.tag_remove successful') - end - list = Tag.tag_list(tags) - test[:verify][:items].each do |key, value| - if value == true - assert(list.include?(key), "Tag verify - should exists but exists #{key}") - else - assert_not(list.include?(key), "Tag verify - exists but should not #{key}") - end - end - end - - # delete tags - tests.each do |test| - tags = test[:tag_add] || test[:tag_remove] - success = Tag.tag_remove(tags) - assert(success, 'Tag.tag_remove successful') - list = Tag.tag_list(tags) - assert_not(list.include?(tags[:item]), 'Tag entry destroyed') - end - end - - test 'tags - real live' do - - ticket1 = Ticket.create( - title: 'some title tag1', - group: Group.lookup(name: 'Users'), - customer_id: 2, - state: Ticket::State.lookup(name: 'new'), - priority: Ticket::Priority.lookup(name: '2 normal'), - updated_by_id: 1, - created_by_id: 1, - ) - ticket2 = Ticket.create( - title: 'some title tag2', - group: Group.lookup(name: 'Users'), - customer_id: 2, - state: Ticket::State.lookup(name: 'new'), - priority: Ticket::Priority.lookup(name: '2 normal'), - updated_by_id: 1, - created_by_id: 1, - ) - travel 2.seconds - - ticket1.tag_add('some tag1', 1) - ticket1.tag_add('some tag2 ', 1) - ticket1.tag_add(' some tag3', 1) - ticket1.tag_add('some TAG4', 1) - ticket1.tag_add(' some tag4', 1) - - ticket2.tag_add('some tag3', 1) - ticket2.tag_add('some TAG4', 1) - ticket2.tag_add('some tag4 ', 1) - - ticket1.tag_remove('some tag1', 1) - - ticket1_lookup1 = Ticket.lookup(id: ticket1.id) - assert_not_equal(ticket1.updated_at.to_s, ticket1_lookup1.updated_at.to_s) - ticket2_lookup1 = Ticket.lookup(id: ticket2.id) - assert_not_equal(ticket2.updated_at.to_s, ticket2_lookup1.updated_at.to_s) - - tags_ticket1 = ticket1.tag_list - assert_equal(4, tags_ticket1.count) - assert(tags_ticket1.include?('some tag2')) - assert(tags_ticket1.include?('some tag3')) - assert(tags_ticket1.include?('some TAG4')) - assert(tags_ticket1.include?('some tag4')) - - tags_ticket2 = ticket2.tag_list - assert_equal(3, tags_ticket2.count) - assert(tags_ticket2.include?('some tag3')) - assert(tags_ticket2.include?('some TAG4')) - assert(tags_ticket2.include?('some tag4')) - - # rename tag - travel 2.seconds - tag_item3 = Tag::Item.find_by(name: 'some tag3') - Tag::Item.rename( - id: tag_item3.id, - name: ' some tag33', - created_by_id: 1, - ) - - ticket1_lookup2 = Ticket.lookup(id: ticket1.id) - assert_not_equal(ticket1_lookup2.updated_at.to_s, ticket1_lookup1.updated_at.to_s) - ticket2_lookup2 = Ticket.lookup(id: ticket2.id) - assert_not_equal(ticket2_lookup2.updated_at.to_s, ticket2_lookup1.updated_at.to_s) - - tags_ticket1 = ticket1.tag_list - assert_equal(4, tags_ticket1.count) - assert(tags_ticket1.include?('some tag2')) - assert(tags_ticket1.include?('some tag33')) - assert(tags_ticket1.include?('some TAG4')) - assert(tags_ticket1.include?('some tag4')) - - tags_ticket2 = ticket2.tag_list - assert_equal(3, tags_ticket2.count) - assert(tags_ticket2.include?('some tag33')) - assert(tags_ticket2.include?('some TAG4')) - assert(tags_ticket2.include?('some tag4')) - - # merge tags - travel 2.seconds - Tag::Item.rename( - id: tag_item3.id, - name: 'some tag2', - created_by_id: 1, - ) - - ticket1_lookup3 = Ticket.lookup(id: ticket1.id) - assert_not_equal(ticket1_lookup3.updated_at.to_s, ticket1_lookup2.updated_at.to_s) - ticket2_lookup3 = Ticket.lookup(id: ticket2.id) - assert_not_equal(ticket2_lookup3.updated_at.to_s, ticket2_lookup2.updated_at.to_s) - - tags_ticket1 = ticket1.tag_list - assert_equal(3, tags_ticket1.count) - assert(tags_ticket1.include?('some tag2')) - assert(tags_ticket1.include?('some TAG4')) - assert(tags_ticket1.include?('some tag4')) - - tags_ticket2 = ticket2.tag_list - assert_equal(3, tags_ticket2.count) - assert(tags_ticket2.include?('some tag2')) - assert(tags_ticket2.include?('some TAG4')) - assert(tags_ticket2.include?('some tag4')) - - assert_not(Tag::Item.find_by(id: tag_item3.id)) - - # remove tag item - travel 2.seconds - tag_item4 = Tag::Item.find_by(name: 'some TAG4') - Tag::Item.remove(tag_item4.id) - - tags_ticket1 = ticket1.tag_list - assert_equal(2, tags_ticket1.count) - assert(tags_ticket1.include?('some tag2')) - assert(tags_ticket1.include?('some tag4')) - - tags_ticket2 = ticket2.tag_list - assert_equal(2, tags_ticket2.count) - assert(tags_ticket2.include?('some tag2')) - assert(tags_ticket2.include?('some tag4')) - - assert_not(Tag::Item.find_by(id: tag_item4.id)) - - ticket1_lookup4 = Ticket.lookup(id: ticket1.id) - assert_not_equal(ticket1_lookup4.updated_at.to_s, ticket1_lookup3.updated_at.to_s) - ticket2_lookup4 = Ticket.lookup(id: ticket2.id) - assert_not_equal(ticket2_lookup4.updated_at.to_s, ticket2_lookup3.updated_at.to_s) - travel_back - end - - test 'tags - rename tag with same name' do - - ticket1 = Ticket.create( - title: 'rename tag1', - group: Group.lookup(name: 'Users'), - customer_id: 2, - updated_by_id: 1, - created_by_id: 1, - ) - ticket2 = Ticket.create( - title: 'rename tag2', - group: Group.lookup(name: 'Users'), - customer_id: 2, - updated_by_id: 1, - created_by_id: 1, - ) - - ticket1.tag_add('some rename tag1', 1) - ticket1.tag_add('some rename tag2 ', 1) - - ticket2.tag_add('some rename tag2', 1) - - tags_ticket1 = ticket1.tag_list - assert_equal(2, tags_ticket1.count) - assert(tags_ticket1.include?('some rename tag1')) - assert(tags_ticket1.include?('some rename tag2')) - - tags_ticket2 = ticket2.tag_list - assert_equal(1, tags_ticket2.count) - assert(tags_ticket2.include?('some rename tag2')) - - tag_item1 = Tag::Item.find_by(name: 'some rename tag1') - Tag::Item.rename( - id: tag_item1.id, - name: ' some rename tag1', - created_by_id: 1, - ) - - tags_ticket1 = ticket1.tag_list - assert_equal(2, tags_ticket1.count) - assert(tags_ticket1.include?('some rename tag1')) - assert(tags_ticket1.include?('some rename tag2')) - - tags_ticket2 = ticket2.tag_list - assert_equal(1, tags_ticket2.count) - assert(tags_ticket2.include?('some rename tag2')) - - end - - test 'tags - rename and merge tag with existing tag' do - - ticket1 = Ticket.create( - title: 'rename tag1', - group: Group.lookup(name: 'Users'), - customer_id: 2, - updated_by_id: 1, - created_by_id: 1, - ) - ticket2 = Ticket.create( - title: 'rename tag2', - group: Group.lookup(name: 'Users'), - customer_id: 2, - updated_by_id: 1, - created_by_id: 1, - ) - - ticket1.tag_add('tagname1', 1) - ticket1.tag_add('tagname2', 1) - - ticket2.tag_add('Tagname2', 1) - - tags_ticket1 = ticket1.tag_list - assert_equal(2, tags_ticket1.count) - assert(tags_ticket1.include?('tagname1')) - assert(tags_ticket1.include?('tagname2')) - - tags_ticket2 = ticket2.tag_list - assert_equal(1, tags_ticket2.count) - assert(tags_ticket2.include?('Tagname2')) - - tag_item1 = Tag::Item.lookup(name: 'Tagname2') - Tag::Item.rename( - id: tag_item1.id, - name: 'tagname2', - created_by_id: 1, - ) - - tags_ticket1 = ticket1.tag_list - assert_equal(2, tags_ticket1.count) - assert(tags_ticket1.include?('tagname1')) - assert(tags_ticket1.include?('tagname2')) - - tags_ticket2 = ticket2.tag_list - assert_equal(1, tags_ticket2.count) - assert(tags_ticket2.include?('tagname2')) - - end -end