tag_spec.rb 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. require 'rails_helper'
  2. RSpec.describe Tag, type: :model do
  3. subject(:tag) { create(:tag) }
  4. describe '.tag_add' do
  5. it 'touches the target object' do
  6. expect { described_class.tag_add(object: 'Ticket', item: 'foo', o_id: Ticket.first.id, created_by_id: 1) }
  7. .to change { Ticket.first.updated_at }
  8. end
  9. context 'when a Tag::Object does not exist for the given class' do
  10. it 'creates it and assigns it to a new Tag' do
  11. expect { described_class.tag_add(object: 'Foo', item: 'bar', o_id: 1, created_by_id: 1) }
  12. .to change(described_class, :count).by(1)
  13. .and change { Tag::Object.exists?(name: 'Foo') }.to(true)
  14. .and change { described_class.last&.tag_object&.name }.to('Foo')
  15. end
  16. end
  17. context 'when a Tag::Object already exists for the given class' do
  18. let!(:tag_object) { Tag::Object.find_or_create_by(name: 'Ticket') }
  19. it 'assigns it to a new Tag' do
  20. expect { described_class.tag_add(object: 'Ticket', item: 'foo', o_id: 1, created_by_id: 1) }
  21. .to change(described_class, :count).by(1)
  22. .and not_change(Tag::Object, :count)
  23. .and change { described_class.last&.tag_object&.name }.to('Ticket')
  24. end
  25. end
  26. context 'when a Tag::Item does not exist with the given name' do
  27. it 'creates it and assigns it to a new Tag' do
  28. expect { described_class.tag_add(object: 'Ticket', item: 'foo', o_id: 1, created_by_id: 1) }
  29. .to change(described_class, :count).by(1)
  30. .and change { Tag::Item.exists?(name: 'foo') }.to(true)
  31. .and change { described_class.last&.tag_item&.name }.to('foo')
  32. end
  33. it 'strips trailing/leading whitespace' do
  34. expect { described_class.tag_add(object: 'Ticket', item: ' foo ', o_id: 1, created_by_id: 1) }
  35. .to change(described_class, :count).by(1)
  36. .and change { Tag::Item.exists?(name: 'foo') }.to(true)
  37. .and change { described_class.last&.tag_item&.name }.to('foo')
  38. end
  39. context 'and the name contains 8-bit Unicode characters' do
  40. it 'creates it and assigns it to a new Tag' do
  41. expect { described_class.tag_add(object: 'Ticket', item: 'fooöäüß', o_id: 1, created_by_id: 1) }
  42. .to change(described_class, :count).by(1)
  43. .and change { Tag::Item.exists?(name: 'fooöäüß') }.to(true)
  44. .and change { described_class.last&.tag_item&.name }.to('fooöäüß')
  45. end
  46. end
  47. context 'but the name is a case-sensitive variant of an existing Tag::Item' do
  48. let!(:tag_item) { create(:'tag/item', name: 'foo') }
  49. it 'creates it and assigns it to a new Tag' do
  50. expect { described_class.tag_add(object: 'Ticket', item: 'FOO', o_id: 1, created_by_id: 1) }
  51. .to change(described_class, :count).by(1)
  52. .and change { Tag::Item.pluck(:name).include?('FOO') }.to(true) # .exists?(name: 'FOO') fails on MySQL
  53. .and change { described_class.last&.tag_item&.name }.to('FOO')
  54. end
  55. end
  56. end
  57. context 'when a Tag::Item already exists with the given name' do
  58. let!(:tag_item) { create(:'tag/item', name: 'foo') }
  59. it 'assigns it to a new Tag' do
  60. expect { described_class.tag_add(object: 'Ticket', item: 'foo', o_id: 1, created_by_id: 1) }
  61. .to change(described_class, :count).by(1)
  62. .and not_change(Tag::Item, :count)
  63. .and change { described_class.last&.tag_item&.name }.to('foo')
  64. end
  65. it 'strips leading/trailing whitespace' do
  66. expect { described_class.tag_add(object: 'Ticket', item: ' foo ', o_id: 1, created_by_id: 1) }
  67. .to change(described_class, :count).by(1)
  68. .and not_change(Tag::Item, :count)
  69. .and change { described_class.last&.tag_item&.name }.to('foo')
  70. end
  71. end
  72. context 'when a Tag already exists for the specified record with the given name' do
  73. let!(:tag) { create(:tag, o: Ticket.first, tag_item: tag_item) }
  74. let(:tag_item) { create(:'tag/item', name: 'foo') }
  75. it 'does not create any records' do
  76. expect { described_class.tag_add(object: 'Ticket', item: 'foo', o_id: Ticket.first.id, created_by_id: 1) }
  77. .to not_change(described_class, :count)
  78. .and not_change(Tag::Item, :count)
  79. end
  80. end
  81. end
  82. describe '.tag_remove' do
  83. it 'touches the target object' do
  84. expect { described_class.tag_remove(object: 'Ticket', item: 'foo', o_id: Ticket.first.id, created_by_id: 1) }
  85. .to change { Ticket.first.updated_at }
  86. end
  87. context 'when a matching Tag exists' do
  88. let!(:tag) { create(:tag, o: Ticket.first, tag_item: tag_item) }
  89. let(:tag_item) { create(:'tag/item', name: 'foo') }
  90. it 'destroys the Tag' do
  91. expect { described_class.tag_remove(object: 'Ticket', o_id: Ticket.first.id, item: 'foo') }
  92. .to change(described_class, :count).by(-1)
  93. end
  94. end
  95. context 'when no matching Tag exists' do
  96. it 'makes no changes' do
  97. expect { described_class.tag_remove(object: 'Ticket', o_id: Ticket.first.id, item: 'foo') }
  98. .not_to change(described_class, :count)
  99. end
  100. end
  101. end
  102. describe '.tag_list' do
  103. context 'with ASCII item names' do
  104. before { items.map { |i| create(:tag, tag_item: i, o: Ticket.first) } }
  105. let(:items) do
  106. [
  107. create(:'tag/item', name: 'foo'),
  108. create(:'tag/item', name: 'bar'),
  109. create(:'tag/item', name: 'BAR'),
  110. ]
  111. end
  112. it 'returns all tag names (case-sensitive) for a given record' do
  113. expect(described_class.tag_list(object: 'Ticket', o_id: Ticket.first.id))
  114. .to match_array(%w[foo bar BAR])
  115. end
  116. end
  117. context 'with Unicode (8-bit) item names' do
  118. before { items.map { |i| create(:tag, tag_item: i, o: Ticket.first) } }
  119. let(:items) do
  120. [
  121. create(:'tag/item', name: 'fooöäüß'),
  122. create(:'tag/item', name: 'baröäüß'),
  123. create(:'tag/item', name: 'BARöäüß'),
  124. ]
  125. end
  126. it 'returns all tag names (case-sensitive) for a given record' do
  127. expect(described_class.tag_list(object: 'Ticket', o_id: Ticket.first.id))
  128. .to match_array(%w[fooöäüß baröäüß BARöäüß])
  129. end
  130. end
  131. end
  132. end