state_spec.rb 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. require 'rails_helper'
  3. require 'models/application_model_examples'
  4. require 'models/concerns/can_be_imported_examples'
  5. require 'models/concerns/has_collection_update_examples'
  6. require 'models/concerns/has_xss_sanitized_note_examples'
  7. RSpec.describe Ticket::State, type: :model do
  8. it_behaves_like 'ApplicationModel'
  9. it_behaves_like 'CanBeImported'
  10. it_behaves_like 'HasCollectionUpdate', collection_factory: :ticket_state
  11. it_behaves_like 'HasXssSanitizedNote', model_factory: :ticket_state
  12. describe 'Default state' do
  13. describe 'of whole table:' do
  14. it 'has default records' do
  15. expect(described_class.pluck(:name))
  16. .to contain_exactly('closed', 'merged', 'new', 'open', 'pending close', 'pending reminder')
  17. end
  18. end
  19. describe 'of "new" state:' do
  20. it 'is the sole #default_create state' do
  21. expect(described_class.where(default_create: true))
  22. .to contain_exactly(described_class.find_by(name: 'new'))
  23. end
  24. end
  25. describe 'of "open" state:' do
  26. it 'is the sole #default_follow_up state' do
  27. expect(described_class.where(default_follow_up: true))
  28. .to contain_exactly(described_class.find_by(name: 'open'))
  29. end
  30. end
  31. end
  32. describe 'Class methods:' do
  33. describe '.by_category' do
  34. it 'looks up states by category' do
  35. expect(described_class.by_category(:open))
  36. .to be_an(ActiveRecord::Relation)
  37. .and include(instance_of(described_class))
  38. end
  39. context 'with invalid category name' do
  40. it 'raises ArgumentError' do
  41. expect { described_class.by_category(:invalidcategoryname) }
  42. .to raise_error(ArgumentError)
  43. end
  44. end
  45. end
  46. end
  47. describe 'Attributes:' do
  48. describe '#default_create' do
  49. let!(:original_default) { described_class.find_by(default_create: true) }
  50. context 'for newly created record' do
  51. subject!(:state) { build(:ticket_state, default_create: default_create) }
  52. context 'when true' do
  53. let(:default_create) { true }
  54. it 'unsets previous default' do
  55. expect { state.save }
  56. .to change { original_default.reload.default_create }.to(false)
  57. .and not_change { described_class.where(default_create: true).count }
  58. end
  59. end
  60. context 'when false' do
  61. let(:default_create) { false }
  62. it 'does not alter existing default' do
  63. expect { state.save }
  64. .to not_change { described_class.find_by(default_create: true) }
  65. .and not_change { described_class.where(default_create: true).count }
  66. end
  67. end
  68. end
  69. context 'for existing record' do
  70. subject!(:state) { create(:ticket_state, default_create: default_create) }
  71. context 'when true' do
  72. let(:default_create) { true }
  73. context 'and updated to false' do
  74. it 'assigns Ticket::State.first as default' do
  75. expect { state.update(default_create: false) }
  76. .to change { described_class.first.default_create }.to(true)
  77. .and not_change { described_class.where(default_create: true).count }
  78. end
  79. end
  80. context 'and destroyed' do
  81. it 'assigns Ticket::State.first as default' do
  82. expect { state.destroy }
  83. .to change { described_class.first.default_create }.to(true)
  84. .and not_change { described_class.where(default_create: true).count }
  85. end
  86. end
  87. end
  88. context 'when false' do
  89. let(:default_create) { false }
  90. context 'and updated to true' do
  91. it 'unsets previous default' do
  92. expect { state.update(default_create: true) }
  93. .to change { original_default.reload.default_create }.to(false)
  94. .and not_change { described_class.where(default_create: true).count }
  95. end
  96. end
  97. context 'and destroyed' do
  98. it 'does not alter existing default' do
  99. expect { state.destroy }
  100. .to not_change { described_class.find_by(default_create: true) }
  101. .and not_change { described_class.where(default_create: true).count }
  102. end
  103. end
  104. end
  105. end
  106. end
  107. describe '#default_follow_up' do
  108. let!(:original_default) { described_class.find_by(default_follow_up: true) }
  109. context 'for newly created record' do
  110. subject!(:state) { build(:ticket_state, default_follow_up: default_follow_up) }
  111. context 'when true' do
  112. let(:default_follow_up) { true }
  113. it 'unsets previous default' do
  114. expect { state.save }
  115. .to change { original_default.reload.default_follow_up }.to(false)
  116. .and not_change { described_class.where(default_follow_up: true).count }
  117. end
  118. end
  119. context 'when false' do
  120. let(:default_follow_up) { false }
  121. it 'does not alter existing default' do
  122. expect { state.save }
  123. .to not_change { described_class.find_by(default_follow_up: true) }
  124. .and not_change { described_class.where(default_follow_up: true).count }
  125. end
  126. end
  127. end
  128. context 'for existing record' do
  129. subject!(:state) { create(:ticket_state, default_follow_up: default_follow_up) }
  130. context 'when true' do
  131. let(:default_follow_up) { true }
  132. context 'and updated to false' do
  133. it 'assigns Ticket::State.first as default' do
  134. expect { state.update(default_follow_up: false) }
  135. .to change { described_class.first.default_follow_up }.to(true)
  136. .and not_change { described_class.where(default_follow_up: true).count }
  137. end
  138. end
  139. context 'and destroyed' do
  140. it 'assigns Ticket::State.first as default' do
  141. expect { state.destroy }
  142. .to change { described_class.first.default_follow_up }.to(true)
  143. .and not_change { described_class.where(default_follow_up: true).count }
  144. end
  145. end
  146. end
  147. context 'when false' do
  148. let(:default_follow_up) { false }
  149. context 'and updated to true' do
  150. it 'unsets previous default' do
  151. expect { state.update(default_follow_up: true) }
  152. .to change { original_default.reload.default_follow_up }.to(false)
  153. .and not_change { described_class.where(default_follow_up: true).count }
  154. end
  155. end
  156. context 'and destroyed' do
  157. it 'does not alter existing default' do
  158. expect { state.destroy }
  159. .to not_change { described_class.find_by(default_follow_up: true) }
  160. .and not_change { described_class.where(default_follow_up: true).count }
  161. end
  162. end
  163. end
  164. end
  165. end
  166. end
  167. describe 'Callbacks' do
  168. let(:attr) { ObjectManager::Attribute.get(object: 'Ticket', name: 'state_id') }
  169. before { Setting.set('system_init_done', true) }
  170. it 'updates state_id attribute when a new state is added' do
  171. new_state = create(:ticket_state, state_type: Ticket::StateType.lookup(name: 'open'))
  172. expect(attr.screens)
  173. .to include(
  174. 'create_middle' => include(
  175. 'ticket.agent' => include('filter' => include(new_state.id)),
  176. 'ticket.customer' => include('filter' => not_include(new_state.id))
  177. ),
  178. 'edit' => include(
  179. 'ticket.agent' => include('filter' => include(new_state.id)),
  180. 'ticket.customer' => include('filter' => include(new_state.id))
  181. )
  182. )
  183. end
  184. context 'with an existing state' do
  185. let(:state) { create(:ticket_state, state_type: Ticket::StateType.lookup(name: 'new')) }
  186. it 'updates state_id attribute when a state is modified' do
  187. state.update! state_type: Ticket::StateType.lookup(name: 'open')
  188. expect(attr.screens)
  189. .to include(
  190. 'create_middle' => include(
  191. 'ticket.agent' => include('filter' => include(state.id)),
  192. 'ticket.customer' => include('filter' => not_include(state.id))
  193. ),
  194. 'edit' => include(
  195. 'ticket.agent' => include('filter' => include(state.id)),
  196. 'ticket.customer' => include('filter' => include(state.id))
  197. )
  198. )
  199. end
  200. it 'updated state_id attribute does not include inactive states (#5268)' do
  201. state.update! active: false
  202. expect(attr.screens)
  203. .to include(
  204. 'create_middle' => include(
  205. 'ticket.agent' => include('filter' => not_include(state.id)),
  206. 'ticket.customer' => include('filter' => not_include(state.id))
  207. ),
  208. 'edit' => include(
  209. 'ticket.agent' => include('filter' => not_include(state.id)),
  210. 'ticket.customer' => include('filter' => not_include(state.id))
  211. )
  212. )
  213. end
  214. it 'updates state_id attribute when a state is destroyed' do
  215. state.destroy!
  216. expect(attr.screens)
  217. .to include(
  218. 'create_middle' => include(
  219. 'ticket.agent' => include('filter' => not_include(state.id)),
  220. 'ticket.customer' => include('filter' => not_include(state.id))
  221. ),
  222. 'edit' => include(
  223. 'ticket.agent' => include('filter' => not_include(state.id)),
  224. 'ticket.customer' => include('filter' => not_include(state.id))
  225. )
  226. )
  227. end
  228. end
  229. end
  230. end