ticket_spec.rb 72 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003
  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/can_csv_import_examples'
  6. require 'models/concerns/can_csv_import_ticket_examples'
  7. require 'models/concerns/checks_core_workflow_examples'
  8. require 'models/concerns/has_history_examples'
  9. require 'models/concerns/has_tags_examples'
  10. require 'models/concerns/has_taskbars_examples'
  11. require 'models/concerns/has_xss_sanitized_note_examples'
  12. require 'models/concerns/has_object_manager_attributes_examples'
  13. require 'models/tag/writes_to_ticket_history_examples'
  14. require 'models/ticket/enqueues_user_ticket_counter_job_examples'
  15. require 'models/ticket/escalation_examples'
  16. require 'models/ticket/resets_pending_time_seconds_examples'
  17. require 'models/ticket/sets_close_time_examples'
  18. require 'models/ticket/sets_last_owner_update_time_examples'
  19. RSpec.describe Ticket, type: :model do
  20. subject(:ticket) { create(:ticket) }
  21. it_behaves_like 'ApplicationModel', can_param: { sample_data_attribute: :title }
  22. it_behaves_like 'CanBeImported'
  23. it_behaves_like 'CanCsvImport'
  24. include_examples 'CanCsvImport - Ticket specific tests'
  25. it_behaves_like 'ChecksCoreWorkflow'
  26. it_behaves_like 'HasHistory', history_relation_object: ['Ticket::Article', 'Mention', 'Ticket::SharedDraftZoom']
  27. it_behaves_like 'HasTags'
  28. it_behaves_like 'TagWritesToTicketHistory'
  29. it_behaves_like 'HasTaskbars'
  30. it_behaves_like 'HasXssSanitizedNote', model_factory: :ticket
  31. it_behaves_like 'HasObjectManagerAttributes'
  32. it_behaves_like 'Ticket::Escalation'
  33. it_behaves_like 'TicketEnqueuesTicketUserTicketCounterJob'
  34. it_behaves_like 'TicketResetsPendingTimeSeconds'
  35. it_behaves_like 'TicketSetsCloseTime'
  36. it_behaves_like 'TicketSetsLastOwnerUpdateTime'
  37. it_behaves_like 'Association clears cache', association: :articles, factory: :ticket_article
  38. describe 'Class methods:' do
  39. describe '.selectors' do
  40. # https://github.com/zammad/zammad/issues/1769
  41. context 'when matching multiple tickets, each with multiple articles' do
  42. let(:tickets) { create_list(:ticket, 2) }
  43. let(:condition) do
  44. {
  45. 'article.from' => {
  46. operator: 'contains',
  47. value: 'blubselector.de',
  48. },
  49. }
  50. end
  51. before do
  52. create(:ticket_article, ticket: tickets.first, from: 'asdf1@blubselector.de')
  53. create(:ticket_article, ticket: tickets.first, from: 'asdf2@blubselector.de')
  54. create(:ticket_article, ticket: tickets.first, from: 'asdf3@blubselector.de')
  55. create(:ticket_article, ticket: tickets.last, from: 'asdf4@blubselector.de')
  56. create(:ticket_article, ticket: tickets.last, from: 'asdf5@blubselector.de')
  57. create(:ticket_article, ticket: tickets.last, from: 'asdf6@blubselector.de')
  58. end
  59. it 'returns a list of unique tickets (i.e., no duplicates)' do
  60. expect(described_class.selectors(condition, limit: 100, access: 'full'))
  61. .to contain_exactly(2, tickets.to_a)
  62. end
  63. end
  64. context 'when customer has multiple organizations' do
  65. let(:organization1) { create(:organization) }
  66. let(:organization2) { create(:organization) }
  67. let(:organization3) { create(:organization) }
  68. let(:customer) { create(:customer, organization: organization1, organizations: [organization2, organization3]) }
  69. let(:ticket1) { create(:ticket, customer: customer, organization: organization1) }
  70. let(:ticket2) { create(:ticket, customer: customer, organization: organization2) }
  71. let(:ticket3) { create(:ticket, customer: customer, organization: organization3) }
  72. before do
  73. ticket1 && ticket2 && ticket3
  74. end
  75. context 'when current user organization is used' do
  76. let(:condition) do
  77. {
  78. 'ticket.organization_id' => {
  79. operator: 'is', # is not
  80. pre_condition: 'current_user.organization_id',
  81. },
  82. }
  83. end
  84. it 'returns the customer tickets' do
  85. expect(described_class.selectors(condition, limit: 100, access: 'full', current_user: customer))
  86. .to contain_exactly(3, include(ticket1, ticket2, ticket3))
  87. end
  88. end
  89. end
  90. end
  91. end
  92. describe 'Instance methods:' do
  93. describe '#merge_to' do
  94. let(:target_ticket) { create(:ticket) }
  95. context 'when source ticket has Links' do
  96. let(:linked_tickets) { create_list(:ticket, 3) }
  97. let(:links) { linked_tickets.map { |l| create(:link, from: ticket, to: l) } }
  98. it 'reassigns all links to the target ticket after merge' do
  99. expect { ticket.merge_to(ticket_id: target_ticket.id, user_id: 1) }
  100. .to change { links.each(&:reload).map(&:link_object_source_value) }
  101. .to(Array.new(3) { target_ticket.id })
  102. end
  103. end
  104. context 'when attempting to cross-merge (i.e., to merge B → A after merging A → B)' do
  105. before { target_ticket.merge_to(ticket_id: ticket.id, user_id: 1) }
  106. it 'raises an error' do
  107. expect { ticket.merge_to(ticket_id: target_ticket.id, user_id: 1) }
  108. .to raise_error('It is not possible to merge into an already merged ticket.')
  109. end
  110. end
  111. context 'when attempting to self-merge (i.e., to merge A → A)' do
  112. it 'raises an error' do
  113. expect { ticket.merge_to(ticket_id: ticket.id, user_id: 1) }
  114. .to raise_error('A ticket cannot be merged into itself.')
  115. end
  116. end
  117. context 'when both tickets are linked with the same parent (parent->child)' do
  118. let(:parent) { create(:ticket) }
  119. before do
  120. create(:link,
  121. link_type: 'child',
  122. link_object_source_value: ticket.id,
  123. link_object_target_value: parent.id)
  124. create(:link,
  125. link_type: 'child',
  126. link_object_source_value: target_ticket.id,
  127. link_object_target_value: parent.id)
  128. ticket.merge_to(ticket_id: target_ticket.id, user_id: 1)
  129. end
  130. it 'does remove the link from the merged ticket' do
  131. links = Link.list(
  132. link_object: 'Ticket',
  133. link_object_value: ticket.id
  134. )
  135. expect(links.count).to eq(1) # one link to the source ticket (no parent link)
  136. end
  137. it 'does not remove the link from the target ticket' do
  138. links = Link.list(
  139. link_object: 'Ticket',
  140. link_object_value: target_ticket.id
  141. )
  142. expect(links.count).to eq(2) # one link to the merged ticket + parent link
  143. end
  144. end
  145. context 'when both tickets are linked with the same parent (child->parent)' do
  146. let(:parent) { create(:ticket) }
  147. before do
  148. create(:link,
  149. link_type: 'child',
  150. link_object_source_value: parent.id,
  151. link_object_target_value: ticket.id)
  152. create(:link,
  153. link_type: 'child',
  154. link_object_source_value: parent.id,
  155. link_object_target_value: target_ticket.id)
  156. ticket.merge_to(ticket_id: target_ticket.id, user_id: 1)
  157. end
  158. it 'does remove the link from the merged ticket' do
  159. links = Link.list(
  160. link_object: 'Ticket',
  161. link_object_value: ticket.id
  162. )
  163. expect(links.count).to eq(1) # one link to the source ticket (no parent link)
  164. end
  165. it 'does not remove the link from the target ticket' do
  166. links = Link.list(
  167. link_object: 'Ticket',
  168. link_object_value: target_ticket.id
  169. )
  170. expect(links.count).to eq(2) # one link to the merged ticket + parent link
  171. end
  172. end
  173. context 'when both tickets are linked with the same parent (different link types)' do
  174. let(:parent) { create(:ticket) }
  175. before do
  176. create(:link,
  177. link_type: 'normal',
  178. link_object_source_value: parent.id,
  179. link_object_target_value: ticket.id)
  180. create(:link,
  181. link_type: 'child',
  182. link_object_source_value: parent.id,
  183. link_object_target_value: target_ticket.id)
  184. ticket.merge_to(ticket_id: target_ticket.id, user_id: 1)
  185. end
  186. it 'does remove the link from the merged ticket' do
  187. links = Link.list(
  188. link_object: 'Ticket',
  189. link_object_value: ticket.id
  190. )
  191. expect(links.count).to eq(1) # one link to the source ticket (no normal link)
  192. end
  193. it 'does not remove the link from the target ticket' do
  194. links = Link.list(
  195. link_object: 'Ticket',
  196. link_object_value: target_ticket.id
  197. )
  198. expect(links.count).to eq(3) # one lin to the merged ticket + parent link + normal link
  199. end
  200. end
  201. context 'when both tickets having mentions to the same user' do
  202. let(:watcher) { create(:agent, groups: [ticket.group, target_ticket.group]) }
  203. before do
  204. create(:mention, mentionable: ticket, user: watcher)
  205. create(:mention, mentionable: target_ticket, user: watcher)
  206. ticket.merge_to(ticket_id: target_ticket.id, user_id: 1)
  207. end
  208. it 'does remove the link from the merged ticket' do
  209. expect(target_ticket.mentions.count).to eq(1) # one mention to watcher user
  210. end
  211. end
  212. context 'when merging a ticket with mentioned user who has no access to the target ticket' do
  213. let(:watcher) { create(:agent, groups: [ticket.group]) }
  214. it 'does remove the link from the merged ticket' do
  215. create(:mention, mentionable: ticket, user: watcher)
  216. expect { ticket.merge_to(ticket_id: target_ticket.id, user_id: 1) }
  217. .to change { target_ticket.mentions.count }
  218. .to(1)
  219. end
  220. end
  221. context 'when merging' do
  222. let(:merge_user) { create(:user) }
  223. before do
  224. # create target ticket early
  225. # to avoid a race condition
  226. # when creating the history entries
  227. target_ticket
  228. travel 5.minutes
  229. ticket.merge_to(ticket_id: target_ticket.id, user_id: merge_user.id)
  230. end
  231. # Issue #2469 - Add information "Ticket merged" to History
  232. it 'creates history entries in both the origin ticket and the target ticket' do
  233. expect(target_ticket.history_get.size).to eq 2
  234. target_history = target_ticket.history_get.last
  235. expect(target_history['object']).to eq 'Ticket'
  236. expect(target_history['type']).to eq 'received_merge'
  237. expect(target_history['created_by_id']).to eq merge_user.id
  238. expect(target_history['o_id']).to eq target_ticket.id
  239. expect(target_history['id_to']).to eq target_ticket.id
  240. expect(target_history['id_from']).to eq ticket.id
  241. expect(ticket.history_get.size).to eq 4
  242. origin_history = ticket.reload.history_get[1]
  243. expect(origin_history['object']).to eq 'Ticket'
  244. expect(origin_history['type']).to eq 'merged_into'
  245. expect(origin_history['created_by_id']).to eq merge_user.id
  246. expect(origin_history['o_id']).to eq ticket.id
  247. expect(origin_history['id_to']).to eq target_ticket.id
  248. expect(origin_history['id_from']).to eq ticket.id
  249. end
  250. it 'sends ExternalSync.migrate' do
  251. allow(ExternalSync).to receive(:migrate)
  252. ticket.merge_to(ticket_id: target_ticket.id, user_id: merge_user.id)
  253. expect(ExternalSync).to have_received(:migrate).with('Ticket', ticket.id, target_ticket.id)
  254. end
  255. # Issue #2960 - Ticket removal of merged / linked tickets doesn't remove references
  256. context 'and deleting the origin ticket' do
  257. it 'adds reference number and title to the target ticket' do
  258. expect { ticket.destroy }
  259. .to change { target_ticket.history_get.find { |elem| elem.fetch('type') == 'received_merge' }['value_from'] }
  260. .to("##{ticket.number} #{ticket.title}")
  261. end
  262. end
  263. # Issue #2960 - Ticket removal of merged / linked tickets doesn't remove references
  264. context 'and deleting the target ticket' do
  265. it 'adds reference number and title to the origin ticket' do
  266. expect { target_ticket.destroy }
  267. .to change { ticket.history_get.find { |elem| elem.fetch('type') == 'merged_into' }['value_to'] }
  268. .to("##{target_ticket.number} #{target_ticket.title}")
  269. end
  270. end
  271. end
  272. # https://github.com/zammad/zammad/issues/3105
  273. context 'when merge actions triggers exist', :performs_jobs do
  274. before do
  275. ticket && target_ticket
  276. merged_into_trigger && received_merge_trigger && update_trigger
  277. allow_any_instance_of(described_class).to receive(:perform_changes) do |ticket, trigger|
  278. log << { ticket: ticket.id, trigger: trigger.id }
  279. end
  280. perform_enqueued_jobs do
  281. ticket.merge_to(ticket_id: target_ticket.id, user_id: 1)
  282. end
  283. end
  284. let(:merged_into_trigger) { create(:trigger, :conditionable, condition_ticket_action: 'update.merged_into') }
  285. let(:received_merge_trigger) { create(:trigger, :conditionable, condition_ticket_action: 'update.received_merge') }
  286. let(:update_trigger) { create(:trigger, :conditionable, condition_ticket_action: 'update') }
  287. let(:log) { [] }
  288. it 'merge_into triggered with source ticket' do
  289. expect(log).to include({ ticket: ticket.id, trigger: merged_into_trigger.id })
  290. end
  291. it 'received_merge not triggered with source ticket' do
  292. expect(log).not_to include({ ticket: ticket.id, trigger: received_merge_trigger.id })
  293. end
  294. it 'update not triggered with source ticket' do
  295. expect(log).not_to include({ ticket: ticket.id, trigger: update_trigger.id })
  296. end
  297. it 'merge_into not triggered with target ticket' do
  298. expect(log).not_to include({ ticket: target_ticket.id, trigger: merged_into_trigger.id })
  299. end
  300. it 'received_merge triggered with target ticket' do
  301. expect(log).to include({ ticket: target_ticket.id, trigger: received_merge_trigger.id })
  302. end
  303. it 'update not triggered with target ticket' do
  304. expect(log).not_to include({ ticket: target_ticket.id, trigger: update_trigger.id })
  305. end
  306. end
  307. # https://github.com/zammad/zammad/issues/3105
  308. context 'when user has notifications enabled', :performs_jobs do
  309. before do
  310. user
  311. allow(OnlineNotification).to receive(:add) do |**args|
  312. next if args[:object] != 'Ticket'
  313. log << { type: :online, event: args[:type], ticket_id: args[:o_id], user_id: args[:user_id] }
  314. end
  315. allow(NotificationFactory::Mailer).to receive(:notification) do |**args|
  316. log << { type: :email, event: args[:template], ticket_id: args[:objects][:ticket].id, user_id: args[:user].id }
  317. end
  318. perform_enqueued_jobs do
  319. ticket.merge_to(ticket_id: target_ticket.id, user_id: 1)
  320. end
  321. end
  322. let(:user) { create(:agent, :preferencable, notification_group_ids: [ticket, target_ticket].map(&:group_id), groups: [ticket, target_ticket].map(&:group)) }
  323. let(:log) { [] }
  324. it 'merge_into notification sent with source ticket' do
  325. expect(log).to include({ type: :online, event: 'update.merged_into', ticket_id: ticket.id, user_id: user.id })
  326. end
  327. it 'received_merge notification not sent with source ticket' do
  328. expect(log).not_to include({ type: :online, event: 'update.received_merge', ticket_id: ticket.id, user_id: user.id })
  329. end
  330. it 'update notification not sent with source ticket' do
  331. expect(log).not_to include({ type: :online, event: 'update', ticket_id: ticket.id, user_id: user.id })
  332. end
  333. it 'merge_into notification not sent with target ticket' do
  334. expect(log).not_to include({ type: :online, event: 'update.merged_into', ticket_id: target_ticket.id, user_id: user.id })
  335. end
  336. it 'received_merge notification sent with target ticket' do
  337. expect(log).to include({ type: :online, event: 'update.received_merge', ticket_id: target_ticket.id, user_id: user.id })
  338. end
  339. it 'update notification not sent with target ticket' do
  340. expect(log).not_to include({ type: :online, event: 'update', ticket_id: target_ticket.id, user_id: user.id })
  341. end
  342. it 'merge_into email sent with source ticket' do
  343. expect(log).to include({ type: :email, event: 'ticket_update_merged_into', ticket_id: ticket.id, user_id: user.id })
  344. end
  345. it 'received_merge email not sent with source ticket' do
  346. expect(log).not_to include({ type: :email, event: 'ticket_update_received_merge', ticket_id: ticket.id, user_id: user.id })
  347. end
  348. it 'update email not sent with source ticket' do
  349. expect(log).not_to include({ type: :email, event: 'ticket_update', ticket_id: ticket.id, user_id: user.id })
  350. end
  351. it 'merge_into email not sent with target ticket' do
  352. expect(log).not_to include({ type: :email, event: 'ticket_update_merged_into', ticket_id: target_ticket.id, user_id: user.id })
  353. end
  354. it 'received_merge email sent with target ticket' do
  355. expect(log).to include({ type: :email, event: 'ticket_update_received_merge', ticket_id: target_ticket.id, user_id: user.id })
  356. end
  357. it 'update email not sent with target ticket' do
  358. expect(log).not_to include({ type: :email, event: 'ticket_update', ticket_id: target_ticket.id, user_id: user.id })
  359. end
  360. end
  361. # https://github.com/zammad/zammad/issues/3105
  362. context 'when sending notification email correct template', :performs_jobs do
  363. before do
  364. user
  365. allow(NotificationFactory::Mailer).to receive(:deliver) do |**args|
  366. log << args[:subject]
  367. end
  368. perform_enqueued_jobs do
  369. ticket.merge_to(ticket_id: target_ticket.id, user_id: 1)
  370. end
  371. end
  372. let(:user) { create(:agent, :preferencable, notification_group_ids: [ticket, target_ticket].map(&:group_id), groups: [ticket, target_ticket].map(&:group)) }
  373. let(:log) { [] }
  374. it 'is used for merged_into' do
  375. expect(log).to include(start_with("Ticket (#{ticket.title}) was merged into another ticket"))
  376. end
  377. it 'is used for received_merge' do
  378. expect(log).to include(start_with("Another ticket was merged into ticket (#{target_ticket.title})"))
  379. end
  380. end
  381. context 'ApplicationHandleInfo context' do
  382. it 'gets switched to "merge"' do
  383. allow(ApplicationHandleInfo).to receive('context=')
  384. ticket.merge_to(ticket_id: target_ticket.id, user_id: 1)
  385. expect(ApplicationHandleInfo).to have_received('context=').with('merge').at_least(1)
  386. end
  387. it 'reverts back to default' do
  388. allow(ApplicationHandleInfo).to receive('context=')
  389. ticket.merge_to(ticket_id: target_ticket.id, user_id: 1)
  390. expect(ApplicationHandleInfo.context).not_to eq 'merge'
  391. end
  392. end
  393. end
  394. describe '#subject_build' do
  395. context 'with default "ticket_hook_position" setting ("right")' do
  396. it 'returns the given string followed by a ticket reference (of the form "[Ticket#123]")' do
  397. expect(ticket.subject_build('foo'))
  398. .to eq("foo [Ticket##{ticket.number}]")
  399. end
  400. context 'and a non-default value for the "ticket_hook" setting' do
  401. before { Setting.set('ticket_hook', 'bar baz') }
  402. it 'replaces "Ticket#" with the new ticket hook' do
  403. expect(ticket.subject_build('foo'))
  404. .to eq("foo [bar baz#{ticket.number}]")
  405. end
  406. end
  407. context 'and a non-default value for the "ticket_hook_divider" setting' do
  408. before { Setting.set('ticket_hook_divider', ': ') }
  409. it 'inserts the new ticket hook divider between "Ticket#" and the ticket number' do
  410. expect(ticket.subject_build('foo'))
  411. .to eq("foo [Ticket#: #{ticket.number}]")
  412. end
  413. end
  414. context 'when the given string already contains a ticket reference, but in the wrong place' do
  415. it 'moves the ticket reference to the end' do
  416. expect(ticket.subject_build("[Ticket##{ticket.number}] foo"))
  417. .to eq("foo [Ticket##{ticket.number}]")
  418. end
  419. end
  420. context 'when the given string already contains an alternately formatted ticket reference' do
  421. it 'reformats the ticket reference' do
  422. expect(ticket.subject_build("foo [Ticket#: #{ticket.number}]"))
  423. .to eq("foo [Ticket##{ticket.number}]")
  424. end
  425. end
  426. end
  427. context 'with alternate "ticket_hook_position" setting ("left")' do
  428. before { Setting.set('ticket_hook_position', 'left') }
  429. it 'returns a ticket reference (of the form "[Ticket#123]") followed by the given string' do
  430. expect(ticket.subject_build('foo'))
  431. .to eq("[Ticket##{ticket.number}] foo")
  432. end
  433. context 'and a non-default value for the "ticket_hook" setting' do
  434. before { Setting.set('ticket_hook', 'bar baz') }
  435. it 'replaces "Ticket#" with the new ticket hook' do
  436. expect(ticket.subject_build('foo'))
  437. .to eq("[bar baz#{ticket.number}] foo")
  438. end
  439. end
  440. context 'and a non-default value for the "ticket_hook_divider" setting' do
  441. before { Setting.set('ticket_hook_divider', ': ') }
  442. it 'inserts the new ticket hook divider between "Ticket#" and the ticket number' do
  443. expect(ticket.subject_build('foo'))
  444. .to eq("[Ticket#: #{ticket.number}] foo")
  445. end
  446. end
  447. context 'when the given string already contains a ticket reference, but in the wrong place' do
  448. it 'moves the ticket reference to the start' do
  449. expect(ticket.subject_build("foo [Ticket##{ticket.number}]"))
  450. .to eq("[Ticket##{ticket.number}] foo")
  451. end
  452. end
  453. context 'when the given string already contains an alternately formatted ticket reference' do
  454. it 'reformats the ticket reference' do
  455. expect(ticket.subject_build("[Ticket#: #{ticket.number}] foo"))
  456. .to eq("[Ticket##{ticket.number}] foo")
  457. end
  458. end
  459. end
  460. end
  461. describe '#last_original_update_at' do
  462. let(:result) { ticket.last_original_update_at }
  463. it 'returns initial customer enquiry time when customer contacted repeatedly' do
  464. ticket
  465. target = create(:ticket_article, :inbound_email, ticket: ticket)
  466. travel 10.minutes
  467. create(:ticket_article, :inbound_email, ticket: ticket)
  468. expect(result).to eq target.created_at
  469. end
  470. it 'returns agent contact time when customer did not respond to agent reach out' do
  471. ticket
  472. create(:ticket_article, :outbound_email, ticket: ticket)
  473. expect(result).to eq ticket.last_contact_agent_at
  474. end
  475. it 'returns nil if no customer response' do
  476. ticket
  477. expect(result).to be_nil
  478. end
  479. context 'with customer enquiry and agent response' do
  480. before do
  481. ticket
  482. create(:ticket_article, :inbound_email, ticket: ticket)
  483. travel 10.minutes
  484. create(:ticket_article, :outbound_email, ticket: ticket)
  485. travel 10.minutes
  486. end
  487. it 'returns last customer enquiry time when agent did not respond yet' do
  488. target = create(:ticket_article, :inbound_email, ticket: ticket)
  489. expect(result).to eq target.created_at
  490. end
  491. it 'returns agent response time when agent responded to customer enquiry' do
  492. expect(result).to eq ticket.last_contact_agent_at
  493. end
  494. end
  495. end
  496. describe '#param_cleanup' do
  497. it 'does only remove parameters which are invalid and not the complete params hash if one element is invalid (#3743)' do
  498. expect(described_class.param_cleanup({ state_id: 3, customer_id: 'guess:1234' }, true, false, false)).to eq({ 'state_id' => 3 })
  499. end
  500. end
  501. end
  502. describe 'Attributes:' do
  503. describe '#owner' do
  504. let(:original_owner) { create(:agent, groups: [ticket.group]) }
  505. before { ticket.update(owner: original_owner) }
  506. context 'when assigned directly' do
  507. context 'to an active agent belonging to ticket.group' do
  508. let(:agent) { create(:agent, groups: [ticket.group]) }
  509. it 'can be set' do
  510. expect { ticket.update(owner: agent) }
  511. .to change { ticket.reload.owner }.to(agent)
  512. end
  513. end
  514. context 'to an agent not belonging to ticket.group' do
  515. let(:agent) { create(:agent, groups: [other_group]) }
  516. let(:other_group) { create(:group) }
  517. it 'resets to default user (id: 1) instead' do
  518. expect { ticket.update(owner: agent) }
  519. .to change { ticket.reload.owner }.to(User.first)
  520. end
  521. end
  522. context 'to an inactive agent' do
  523. let(:agent) { create(:agent, groups: [ticket.group], active: false) }
  524. it 'resets to default user (id: 1) instead' do
  525. expect { ticket.update(owner: agent) }
  526. .to change { ticket.reload.owner }.to(User.first)
  527. end
  528. end
  529. context 'to a non-agent' do
  530. let(:agent) { create(:customer, groups: [ticket.group]) }
  531. it 'resets to default user (id: 1) instead' do
  532. expect { ticket.update(owner: agent) }
  533. .to change { ticket.reload.owner }.to(User.first)
  534. end
  535. end
  536. end
  537. context 'when the ticket is updated for any other reason' do
  538. context 'if original owner is still an active agent belonging to ticket.group' do
  539. it 'does not change' do
  540. expect { create(:ticket_article, ticket: ticket) }
  541. .not_to change { ticket.reload.owner }
  542. end
  543. end
  544. context 'if original owner has left ticket.group' do
  545. before { original_owner.groups = [] }
  546. it 'resets to default user (id: 1)' do
  547. expect { create(:ticket_article, ticket: ticket) }
  548. .to change { ticket.reload.owner }.to(User.first)
  549. end
  550. end
  551. context 'if original owner has become inactive' do
  552. before { original_owner.update(active: false) }
  553. it 'resets to default user (id: 1)' do
  554. expect { create(:ticket_article, ticket: ticket) }
  555. .to change { ticket.reload.owner }.to(User.first)
  556. end
  557. end
  558. context 'if original owner has lost agent status' do
  559. before { original_owner.roles = create_list(:role, 1) }
  560. it 'resets to default user (id: 1)' do
  561. Rails.cache.clear
  562. expect { create(:ticket_article, ticket: ticket) }
  563. .to change { ticket.reload.owner }.to(User.first)
  564. end
  565. end
  566. context 'when the Ticket is closed' do
  567. before do
  568. ticket.update!(state: Ticket::State.lookup(name: 'closed'))
  569. end
  570. context 'if original owner is still an active agent belonging to ticket.group' do
  571. it 'does not change' do
  572. expect { create(:ticket_article, ticket: ticket) }
  573. .not_to change { ticket.reload.owner }
  574. end
  575. end
  576. context 'if original owner has left ticket.group' do
  577. before { original_owner.groups = [] }
  578. it 'does not change' do
  579. expect { create(:ticket_article, ticket: ticket) }
  580. .not_to change { ticket.reload.owner }
  581. end
  582. end
  583. context 'if original owner has become inactive' do
  584. before { original_owner.update(active: false) }
  585. it 'does not change' do
  586. expect { create(:ticket_article, ticket: ticket) }
  587. .not_to change { ticket.reload.owner }
  588. end
  589. end
  590. context 'if original owner has lost agent status' do
  591. before { original_owner.roles = create_list(:role, 1) }
  592. it 'does not change' do
  593. expect { create(:ticket_article, ticket: ticket) }
  594. .not_to change { ticket.reload.owner }
  595. end
  596. end
  597. end
  598. end
  599. end
  600. describe '#state' do
  601. context 'when originally "new" (default)' do
  602. context 'and a customer article is added' do
  603. let(:article) { create(:ticket_article, ticket: ticket, sender_name: 'Customer') }
  604. it 'stays "new"' do
  605. expect { article }
  606. .not_to change { ticket.state.name }.from('new')
  607. end
  608. end
  609. context 'and a non-customer article is added' do
  610. let(:article) { create(:ticket_article, ticket: ticket, sender_name: 'Agent') }
  611. it 'switches to "open"' do
  612. expect { article }
  613. .to change { ticket.reload.state.name }.from('new').to('open')
  614. end
  615. end
  616. end
  617. context 'when originally "closed"' do
  618. before { ticket.update(state: Ticket::State.find_by(name: 'closed')) }
  619. context 'when a non-customer article is added' do
  620. let(:article) { create(:ticket_article, ticket: ticket, sender_name: 'Agent') }
  621. it 'stays "closed"' do
  622. expect { article }.not_to change { ticket.reload.state.name }
  623. end
  624. end
  625. end
  626. end
  627. describe '#pending_time' do
  628. subject(:ticket) { create(:ticket, pending_time: 2.days.from_now) }
  629. context 'when #state is updated to any non-"pending" value' do
  630. it 'is reset to nil' do
  631. expect { ticket.update!(state: Ticket::State.lookup(name: 'open')) }
  632. .to change(ticket, :pending_time).to(nil)
  633. end
  634. end
  635. # Regression test for commit 92f227786f298bad1ccaf92d4478a7062ea6a49f
  636. context 'when #state is updated to nil (violating DB NOT NULL constraint)' do
  637. it 'does not prematurely raise within the callback (#reset_pending_time)' do
  638. expect { ticket.update!(state: nil) }
  639. .to raise_error(ActiveRecord::StatementInvalid)
  640. end
  641. end
  642. end
  643. describe '#escalation_at' do
  644. before { freeze_time } # freeze time
  645. let(:sla) { create(:sla, calendar: calendar, first_response_time: 60, response_time: 180, solution_time: 240) }
  646. let(:calendar) { create(:calendar, :'24/7') }
  647. context 'with no SLAs in the system' do
  648. it 'defaults to nil' do
  649. expect(ticket.escalation_at).to be_nil
  650. end
  651. end
  652. context 'with an SLA in the system' do
  653. before { sla } # create sla
  654. it 'is set based on SLA’s #first_response_time' do
  655. expect(ticket.reload.escalation_at.to_i)
  656. .to eq(1.hour.from_now.to_i)
  657. end
  658. context 'after first agent’s response' do
  659. before { ticket } # create ticket
  660. let(:article) { create(:ticket_article, ticket: ticket, sender_name: 'Agent') }
  661. it 'is updated based on the SLA’s #close_escalation_at' do
  662. travel(1.minute) # time is frozen: if we don't travel forward, pre- and post-update values will be the same
  663. expect { article }
  664. .to change { ticket.reload.escalation_at }
  665. .to(ticket.reload.close_escalation_at)
  666. end
  667. context 'when new #update_time is later than original #solution_time' do
  668. it 'is updated based on the original #solution_time' do
  669. travel(2.hours) # time is frozen: if we don't travel forward, pre- and post-update values will be the same
  670. expect { article }
  671. .to change { ticket.reload.escalation_at }
  672. .to(4.hours.after(ticket.created_at))
  673. end
  674. end
  675. end
  676. end
  677. context 'when updated after an SLA has been added to the system' do
  678. before do
  679. ticket # create ticket
  680. sla # create sla
  681. end
  682. it 'is updated based on the new SLA’s #first_response_time' do
  683. expect { ticket.save! }
  684. .to change { ticket.reload.escalation_at.to_i }.from(0).to(1.hour.from_now.to_i)
  685. end
  686. end
  687. context 'when updated after all SLAs have been removed from the system' do
  688. before do
  689. sla # create sla
  690. ticket # create ticket
  691. sla.destroy
  692. end
  693. it 'is set to nil' do
  694. expect { ticket.save! }
  695. .to change { ticket.reload.escalation_at }.to(nil)
  696. end
  697. end
  698. context 'when within last (relative)' do
  699. let(:first_response_time) { 5 }
  700. let(:sla) { create(:sla, calendar: calendar, first_response_time: first_response_time) }
  701. let(:within_condition) do
  702. { 'ticket.escalation_at'=>{ 'operator' => 'within last (relative)', 'value' => '30', 'range' => 'minute' } }
  703. end
  704. before do
  705. sla
  706. travel_to '2020-11-05 11:37:00'
  707. ticket = create(:ticket)
  708. create(:ticket_article, :inbound_email, ticket: ticket)
  709. travel_to '2020-11-05 11:50:00'
  710. end
  711. context 'when in range' do
  712. it 'does find the ticket' do
  713. count, _tickets = described_class.selectors(within_condition, limit: 2_000, execution_time: true)
  714. expect(count).to eq(1)
  715. end
  716. end
  717. context 'when out of range' do
  718. let(:first_response_time) { 500 }
  719. it 'does not find the ticket' do
  720. count, _tickets = described_class.selectors(within_condition, limit: 2_000, execution_time: true)
  721. expect(count).to eq(0)
  722. end
  723. end
  724. end
  725. context 'when till (relative)' do
  726. let(:first_response_time) { 5 }
  727. let(:sla) { create(:sla, calendar: calendar, first_response_time: first_response_time) }
  728. let(:condition) do
  729. { 'ticket.escalation_at'=>{ 'operator' => 'till (relative)', 'value' => '30', 'range' => 'minute' } }
  730. end
  731. before do
  732. sla
  733. travel_to '2020-11-05 11:37:00'
  734. ticket = create(:ticket)
  735. create(:ticket_article, :inbound_email, ticket: ticket)
  736. travel_to '2020-11-05 11:50:00'
  737. end
  738. context 'when in range' do
  739. it 'does find the ticket' do
  740. count, _tickets = described_class.selectors(condition, limit: 2_000, execution_time: true)
  741. expect(count).to eq(1)
  742. end
  743. end
  744. context 'when out of range' do
  745. let(:first_response_time) { 500 }
  746. it 'does not find the ticket' do
  747. count, _tickets = described_class.selectors(condition, limit: 2_000, execution_time: true)
  748. expect(count).to eq(0)
  749. end
  750. end
  751. end
  752. context 'when from (relative)' do
  753. let(:first_response_time) { 5 }
  754. let(:sla) { create(:sla, calendar: calendar, first_response_time: first_response_time) }
  755. let(:condition) do
  756. { 'ticket.escalation_at'=>{ 'operator' => 'from (relative)', 'value' => '30', 'range' => 'minute' } }
  757. end
  758. before do
  759. sla
  760. travel_to '2020-11-05 11:37:00'
  761. ticket = create(:ticket)
  762. create(:ticket_article, :inbound_email, ticket: ticket)
  763. end
  764. context 'when in range' do
  765. it 'does find the ticket' do
  766. travel_to '2020-11-05 11:50:00'
  767. count, _tickets = described_class.selectors(condition, limit: 2_000, execution_time: true)
  768. expect(count).to eq(1)
  769. end
  770. end
  771. context 'when out of range' do
  772. let(:first_response_time) { 5 }
  773. it 'does not find the ticket' do
  774. travel_to '2020-11-05 13:50:00'
  775. count, _tickets = described_class.selectors(condition, limit: 2_000, execution_time: true)
  776. expect(count).to eq(0)
  777. end
  778. end
  779. end
  780. context 'when within next (relative)' do
  781. let(:first_response_time) { 5 }
  782. let(:sla) { create(:sla, calendar: calendar, first_response_time: first_response_time) }
  783. let(:within_condition) do
  784. { 'ticket.escalation_at'=>{ 'operator' => 'within next (relative)', 'value' => '30', 'range' => 'minute' } }
  785. end
  786. before do
  787. sla
  788. travel_to '2020-11-05 11:50:00'
  789. ticket = create(:ticket)
  790. create(:ticket_article, :inbound_email, ticket: ticket)
  791. travel_to '2020-11-05 11:37:00'
  792. end
  793. context 'when in range' do
  794. it 'does find the ticket' do
  795. count, _tickets = described_class.selectors(within_condition, limit: 2_000, execution_time: true)
  796. expect(count).to eq(1)
  797. end
  798. end
  799. context 'when out of range' do
  800. let(:first_response_time) { 500 }
  801. it 'does not find the ticket' do
  802. count, _tickets = described_class.selectors(within_condition, limit: 2_000, execution_time: true)
  803. expect(count).to eq(0)
  804. end
  805. end
  806. end
  807. end
  808. describe '#first_response_escalation_at' do
  809. before { freeze_time } # freeze time
  810. let(:sla) { create(:sla, calendar: calendar, first_response_time: 60, response_time: 180, solution_time: 240) }
  811. let(:calendar) { create(:calendar, :'24/7') }
  812. context 'with no SLAs in the system' do
  813. it 'defaults to nil' do
  814. expect(ticket.first_response_escalation_at).to be_nil
  815. end
  816. end
  817. context 'with an SLA in the system' do
  818. before { sla } # create sla
  819. it 'is set based on SLA’s #first_response_time' do
  820. expect(ticket.reload.first_response_escalation_at.to_i)
  821. .to eq(1.hour.from_now.to_i)
  822. end
  823. context 'after first agent’s response' do
  824. before { ticket } # create ticket
  825. let(:article) { create(:ticket_article, ticket: ticket, sender_name: 'Agent') }
  826. it 'is cleared' do
  827. expect { article }.to change { ticket.reload.first_response_escalation_at }.to(nil)
  828. end
  829. end
  830. end
  831. end
  832. describe '#update_escalation_at' do
  833. before { freeze_time } # freeze time
  834. let(:sla) { create(:sla, calendar: calendar, first_response_time: 60, response_time: 180, solution_time: 240) }
  835. let(:calendar) { create(:calendar, :'24/7') }
  836. context 'with no SLAs in the system' do
  837. it 'defaults to nil' do
  838. expect(ticket.update_escalation_at).to be_nil
  839. end
  840. end
  841. context 'with an SLA in the system' do
  842. before { sla } # create sla
  843. it 'is set based on SLA’s #update_time' do
  844. travel 1.minute
  845. create(:ticket_article, ticket: ticket, sender_name: 'Customer')
  846. expect(ticket.reload.update_escalation_at.to_i)
  847. .to eq(3.hours.from_now.to_i)
  848. end
  849. context 'after first agent’s response' do
  850. before { ticket } # create ticket
  851. let(:article) { create(:ticket_article, ticket: ticket, sender_name: 'Agent') }
  852. it 'is updated based on the SLA’s #update_time' do
  853. create(:ticket_article, ticket: ticket, sender_name: 'Customer')
  854. travel(1.minute)
  855. expect { article }
  856. .to change { ticket.reload.update_escalation_at }
  857. .to(nil)
  858. end
  859. end
  860. end
  861. end
  862. describe '#close_escalation_at' do
  863. before { freeze_time } # freeze time
  864. let(:sla) { create(:sla, calendar: calendar, first_response_time: 60, response_time: 180, solution_time: 240) }
  865. let(:calendar) { create(:calendar, :'24/7') }
  866. context 'with no SLAs in the system' do
  867. it 'defaults to nil' do
  868. expect(ticket.close_escalation_at).to be_nil
  869. end
  870. end
  871. context 'with an SLA in the system' do
  872. before { sla } # create sla
  873. it 'is set based on SLA’s #solution_time' do
  874. expect(ticket.reload.close_escalation_at.to_i)
  875. .to eq(4.hours.from_now.to_i)
  876. end
  877. context 'after first agent’s response' do
  878. before { ticket } # create ticket
  879. let(:article) { create(:ticket_article, ticket: ticket, sender_name: 'Agent') }
  880. it 'does not change' do
  881. expect { article }.not_to change(ticket, :close_escalation_at)
  882. end
  883. end
  884. end
  885. end
  886. end
  887. describe '.search' do
  888. shared_examples 'search permissions' do
  889. let(:group) { create(:group) }
  890. before do
  891. ticket
  892. end
  893. shared_examples 'permitted' do
  894. it 'finds Ticket' do
  895. expect(described_class.search(query: ticket.number, current_user: current_user).count).to eq(1)
  896. end
  897. end
  898. shared_examples 'no permission' do
  899. it "doesn't find Ticket" do
  900. expect(described_class.search(query: ticket.number, current_user: current_user)).to be_blank
  901. end
  902. end
  903. context 'Agent with Group access' do
  904. let(:ticket) do
  905. ticket = create(:ticket, group: group)
  906. create(:ticket_article, ticket: ticket)
  907. ticket
  908. end
  909. let(:current_user) { create(:agent, groups: [group]) }
  910. it_behaves_like 'permitted'
  911. end
  912. context 'when Agent is Customer of Ticket' do
  913. let(:ticket) do
  914. ticket = create(:ticket, customer: current_user)
  915. create(:ticket_article, ticket: ticket)
  916. ticket
  917. end
  918. let(:current_user) { create(:agent_and_customer) }
  919. it_behaves_like 'permitted'
  920. end
  921. context 'for Organization access' do
  922. let(:ticket) do
  923. ticket = create(:ticket, customer: customer)
  924. create(:ticket_article, ticket: ticket)
  925. ticket
  926. end
  927. let(:customer) { create(:customer, organization: organization) }
  928. context 'when Organization is shared' do
  929. let(:organization) { create(:organization, shared: true) }
  930. context 'for unrelated Agent' do
  931. let(:current_user) { create(:agent) }
  932. it_behaves_like 'no permission'
  933. end
  934. context 'for Agent in same Organization' do
  935. let(:current_user) { create(:agent_and_customer, organization: organization) }
  936. it_behaves_like 'permitted'
  937. end
  938. context 'for Customer of Ticket' do
  939. let(:current_user) { customer }
  940. it_behaves_like 'permitted'
  941. end
  942. end
  943. context 'when Organization is not shared' do
  944. let(:organization) { create(:organization, shared: false) }
  945. context 'for unrelated Agent' do
  946. let(:current_user) { create(:agent) }
  947. it_behaves_like 'no permission'
  948. end
  949. context 'for Agent in same Organization' do
  950. let(:current_user) { create(:agent_and_customer, organization: organization) }
  951. it_behaves_like 'no permission'
  952. end
  953. context 'for Customer of Ticket' do
  954. let(:current_user) { customer }
  955. it_behaves_like 'permitted'
  956. end
  957. end
  958. end
  959. end
  960. context 'with searchindex', searchindex: true do
  961. include_examples 'search permissions' do
  962. before do
  963. searchindex_model_reload([described_class])
  964. end
  965. end
  966. end
  967. context 'without searchindex' do
  968. before do
  969. Setting.set('es_url', nil)
  970. end
  971. include_examples 'search permissions'
  972. end
  973. end
  974. describe 'Callbacks & Observers -' do
  975. describe 'NULL byte handling (via ChecksAttributeValuesAndLength concern):' do
  976. it 'removes them from title on creation, if necessary (postgres doesn’t like them)' do
  977. expect { create(:ticket, title: "some title \u0000 123") }
  978. .not_to raise_error
  979. end
  980. end
  981. describe 'XSS protection:' do
  982. subject(:ticket) { create(:ticket, title: title) }
  983. let(:title) { 'test 123 <script type="text/javascript">alert("XSS!");</script>' }
  984. it 'does not sanitize title' do
  985. expect(ticket.title).to eq(title)
  986. end
  987. end
  988. describe 'Cti::CallerId syncing:', performs_jobs: true do
  989. subject(:ticket) { build(:ticket) }
  990. before { allow(Cti::CallerId).to receive(:build) }
  991. it 'adds numbers in article bodies (via Cti::CallerId.build)' do
  992. expect(Cti::CallerId).to receive(:build).with(ticket)
  993. ticket.save
  994. perform_enqueued_jobs commit_transaction: true
  995. end
  996. end
  997. describe 'Touching associations on update:' do
  998. subject(:ticket) { create(:ticket, customer: customer) }
  999. let(:customer) { create(:customer, organization: organization) }
  1000. let(:organization) { create(:organization) }
  1001. let(:other_customer) { create(:customer, organization: other_organization) }
  1002. let(:other_organization) { create(:organization) }
  1003. context 'on creation' do
  1004. it 'touches its customer and his organization' do
  1005. expect { ticket }
  1006. .to change { customer.reload.updated_at }
  1007. .and change { organization.reload.updated_at }
  1008. end
  1009. end
  1010. context 'on destruction' do
  1011. before { ticket }
  1012. it 'touches its customer and his organization' do
  1013. expect { ticket.destroy }
  1014. .to change { customer.reload.updated_at }
  1015. .and change { organization.reload.updated_at }
  1016. end
  1017. end
  1018. context 'when customer association is changed' do
  1019. it 'touches both old and new customer, and their organizations' do
  1020. expect { ticket.update(customer: other_customer) }
  1021. .to change { customer.reload.updated_at }
  1022. .and change { organization.reload.updated_at }
  1023. .and change { other_customer.reload.updated_at }
  1024. .and change { other_organization.reload.updated_at }
  1025. end
  1026. end
  1027. end
  1028. describe 'Association & attachment management:' do
  1029. it 'deletes all related ActivityStreams on destroy' do
  1030. create_list(:activity_stream, 3, o: ticket)
  1031. expect { ticket.destroy }
  1032. .to change { ActivityStream.exists?(activity_stream_object_id: ObjectLookup.by_name('Ticket'), o_id: ticket.id) }
  1033. .to(false)
  1034. end
  1035. it 'deletes all related Links on destroy' do
  1036. create(:link, from: ticket, to: create(:ticket))
  1037. create(:link, from: create(:ticket), to: ticket)
  1038. create(:link, from: ticket, to: create(:ticket))
  1039. expect { ticket.destroy }
  1040. .to change { Link.where('link_object_source_value = :id OR link_object_target_value = :id', id: ticket.id).any? }
  1041. .to(false)
  1042. end
  1043. it 'deletes all related Articles on destroy' do
  1044. create_list(:ticket_article, 3, ticket: ticket)
  1045. expect { ticket.destroy }
  1046. .to change { Ticket::Article.exists?(ticket: ticket) }
  1047. .to(false)
  1048. end
  1049. it 'deletes all related OnlineNotifications on destroy' do
  1050. create_list(:online_notification, 3, o: ticket)
  1051. expect { ticket.destroy }
  1052. .to change { OnlineNotification.where(object_lookup_id: ObjectLookup.by_name('Ticket'), o_id: ticket.id).any? }
  1053. .to(false)
  1054. end
  1055. it 'deletes all related Tags on destroy' do
  1056. create_list(:tag, 3, o: ticket)
  1057. expect { ticket.destroy }
  1058. .to change { Tag.exists?(tag_object_id: Tag::Object.lookup(name: 'Ticket').id, o_id: ticket.id) }
  1059. .to(false)
  1060. end
  1061. it 'deletes all related Histories on destroy' do
  1062. create_list(:history, 3, o: ticket)
  1063. expect { ticket.destroy }
  1064. .to change { History.exists?(history_object_id: History::Object.lookup(name: 'Ticket').id, o_id: ticket.id) }
  1065. .to(false)
  1066. end
  1067. it 'deletes all related RecentViews on destroy' do
  1068. create_list(:recent_view, 3, o: ticket)
  1069. expect { ticket.destroy }
  1070. .to change { RecentView.exists?(recent_view_object_id: ObjectLookup.by_name('Ticket'), o_id: ticket.id) }
  1071. .to(false)
  1072. end
  1073. it 'destroys all related dependencies' do
  1074. refs_known = { 'Ticket::Article' => { 'ticket_id'=>1 },
  1075. 'Ticket::TimeAccounting' => { 'ticket_id'=>1 },
  1076. 'Ticket::SharedDraftZoom' => { 'ticket_id'=>0 },
  1077. 'Ticket::Flag' => { 'ticket_id'=>1 } }
  1078. ticket = create(:ticket)
  1079. article = create(:ticket_article, ticket: ticket)
  1080. accounting = create(:ticket_time_accounting, ticket: ticket)
  1081. flag = create(:ticket_flag, ticket: ticket)
  1082. refs_ticket = Models.references('Ticket', ticket.id, true)
  1083. expect(refs_ticket).to eq(refs_known)
  1084. ticket.destroy
  1085. expect { ticket.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  1086. expect { article.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  1087. expect { accounting.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  1088. expect { flag.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  1089. end
  1090. context 'when ticket is generated from email (with attachments)' do
  1091. subject(:ticket) { Channel::EmailParser.new.process({}, raw_email).first }
  1092. let(:raw_email) { Rails.root.join('test/data/mail/mail001.box').read }
  1093. it 'adds attachments to the Store{::File,::Provider::DB} tables' do
  1094. expect { ticket }
  1095. .to change(Store, :count).by(2)
  1096. .and change(Store::File, :count).by(2)
  1097. .and change(Store::Provider::DB, :count).by(2)
  1098. end
  1099. context 'and subsequently destroyed' do
  1100. it 'deletes all related attachments' do
  1101. ticket # create ticket
  1102. expect { ticket.destroy }
  1103. .to change(Store, :count).by(-2)
  1104. .and change(Store::File, :count).by(-2)
  1105. .and change(Store::Provider::DB, :count).by(-2)
  1106. end
  1107. end
  1108. context 'and a duplicate ticket is generated from the same email' do
  1109. before { ticket } # create ticket
  1110. let(:duplicate) { Channel::EmailParser.new.process({}, raw_email).first }
  1111. it 'adds duplicate attachments to the Store table only' do
  1112. expect { duplicate }
  1113. .to change(Store, :count).by(2)
  1114. .and not_change(Store::File, :count)
  1115. .and not_change(Store::Provider::DB, :count)
  1116. end
  1117. context 'when only the duplicate ticket is destroyed' do
  1118. it 'deletes only the duplicate attachments' do
  1119. duplicate # create ticket
  1120. expect { duplicate.destroy }
  1121. .to change(Store, :count).by(-2)
  1122. .and not_change(Store::File, :count)
  1123. .and not_change(Store::Provider::DB, :count)
  1124. end
  1125. it 'deletes all related attachments' do
  1126. duplicate.destroy
  1127. expect { ticket.destroy }
  1128. .to change(Store, :count).by(-2)
  1129. .and change(Store::File, :count).by(-2)
  1130. .and change(Store::Provider::DB, :count).by(-2)
  1131. end
  1132. end
  1133. end
  1134. end
  1135. end
  1136. describe 'Ticket lifecycle order-of-operations:', performs_jobs: true do
  1137. subject!(:ticket) { create(:ticket) }
  1138. let!(:agent) { create(:agent, groups: [group]) }
  1139. let(:group) { create(:group) }
  1140. before do
  1141. create(
  1142. :trigger,
  1143. condition: { 'ticket.action' => { 'operator' => 'is', 'value' => 'create' } },
  1144. perform: { 'ticket.group_id' => { 'value' => group.id } }
  1145. )
  1146. end
  1147. it 'fires triggers before new ticket notifications are sent' do
  1148. expect { TransactionDispatcher.commit }
  1149. .to change { ticket.reload.group }.to(group)
  1150. expect { perform_enqueued_jobs }
  1151. .to change { NotificationFactory::Mailer.already_sent?(ticket, agent, 'email') }.to(1)
  1152. end
  1153. end
  1154. describe 'Ticket has changed attributes:' do
  1155. subject!(:ticket) { create(:ticket) }
  1156. let(:group) { create(:group) }
  1157. let(:condition_field) { nil }
  1158. shared_examples 'updated ticket group with trigger condition' do
  1159. it 'updated ticket group with has changed trigger condition' do
  1160. expect { TransactionDispatcher.commit }.to change { ticket.reload.group }.to(group)
  1161. end
  1162. end
  1163. before do
  1164. create(
  1165. :trigger,
  1166. condition: { "ticket.#{condition_field}" => { 'operator' => 'has changed', 'value' => 'create' } },
  1167. perform: { 'ticket.group_id' => { 'value' => group.id } }
  1168. )
  1169. ticket.update!(condition_field => Time.zone.now)
  1170. end
  1171. context "when changing 'first_response_at' attribute" do
  1172. let(:condition_field) { 'first_response_at' }
  1173. include_examples 'updated ticket group with trigger condition'
  1174. end
  1175. context "when changing 'close_at' attribute" do
  1176. let(:condition_field) { 'close_at' }
  1177. include_examples 'updated ticket group with trigger condition'
  1178. end
  1179. context "when changing 'last_contact_agent_at' attribute" do
  1180. let(:condition_field) { 'last_contact_agent_at' }
  1181. include_examples 'updated ticket group with trigger condition'
  1182. end
  1183. context "when changing 'last_contact_customer_at' attribute" do
  1184. let(:condition_field) { 'last_contact_customer_at' }
  1185. include_examples 'updated ticket group with trigger condition'
  1186. end
  1187. context "when changing 'last_contact_at' attribute" do
  1188. let(:condition_field) { 'last_contact_at' }
  1189. include_examples 'updated ticket group with trigger condition'
  1190. end
  1191. end
  1192. end
  1193. describe 'Mentions:', sends_notification_emails: true do
  1194. context 'when notifications', performs_jobs: true do
  1195. let(:prefs_matrix_no_mentions) do
  1196. { 'notification_config' =>
  1197. { 'matrix' =>
  1198. { 'create' => { 'criteria' => { 'owned_by_me' => true, 'owned_by_nobody' => true, 'subscribed' => false, 'no' => true }, 'channel' => { 'email' => true, 'online' => true } },
  1199. 'update' => { 'criteria' => { 'owned_by_me' => true, 'owned_by_nobody' => true, 'subscribed' => false, 'no' => true }, 'channel' => { 'email' => true, 'online' => true } },
  1200. 'reminder_reached' => { 'criteria' => { 'owned_by_me' => false, 'owned_by_nobody' => false, 'subscribed' => false, 'no' => false }, 'channel' => { 'email' => false, 'online' => false } },
  1201. 'escalation' => { 'criteria' => { 'owned_by_me' => false, 'owned_by_nobody' => false, 'subscribed' => false, 'no' => false }, 'channel' => { 'email' => false, 'online' => false } } } } }
  1202. end
  1203. let(:prefs_matrix_only_mentions) do
  1204. { 'notification_config' =>
  1205. { 'matrix' =>
  1206. { 'create' => { 'criteria' => { 'owned_by_me' => false, 'owned_by_nobody' => false, 'subscribed' => true, 'no' => false }, 'channel' => { 'email' => true, 'online' => true } },
  1207. 'update' => { 'criteria' => { 'owned_by_me' => false, 'owned_by_nobody' => false, 'subscribed' => true, 'no' => false }, 'channel' => { 'email' => true, 'online' => true } },
  1208. 'reminder_reached' => { 'criteria' => { 'owned_by_me' => false, 'owned_by_nobody' => false, 'subscribed' => true, 'no' => false }, 'channel' => { 'email' => false, 'online' => false } },
  1209. 'escalation' => { 'criteria' => { 'owned_by_me' => false, 'owned_by_nobody' => false, 'subscribed' => true, 'no' => false }, 'channel' => { 'email' => false, 'online' => false } } } } }
  1210. end
  1211. let(:prefs_matrix_only_mentions_groups) do
  1212. { 'notification_config' =>
  1213. { 'matrix' =>
  1214. { 'create' => { 'criteria' => { 'owned_by_me' => false, 'owned_by_nobody' => false, 'subscribed' => true, 'no' => false }, 'channel' => { 'email' => true, 'online' => true } },
  1215. 'update' => { 'criteria' => { 'owned_by_me' => false, 'owned_by_nobody' => false, 'subscribed' => true, 'no' => false }, 'channel' => { 'email' => true, 'online' => true } },
  1216. 'reminder_reached' => { 'criteria' => { 'owned_by_me' => false, 'owned_by_nobody' => false, 'subscribed' => true, 'no' => false }, 'channel' => { 'email' => false, 'online' => false } },
  1217. 'escalation' => { 'criteria' => { 'owned_by_me' => false, 'owned_by_nobody' => false, 'subscribed' => true, 'no' => false }, 'channel' => { 'email' => false, 'online' => false } } },
  1218. 'group_ids' => [create(:group).id, create(:group).id, create(:group).id] } }
  1219. end
  1220. let(:mention_group) { create(:group) }
  1221. let(:no_access_group) { create(:group) }
  1222. let(:user_only_mentions) { create(:agent, groups: [mention_group], preferences: prefs_matrix_only_mentions) }
  1223. let(:user_read_mentions) { create(:agent, groups: [mention_group], preferences: prefs_matrix_only_mentions_groups) }
  1224. let(:user_no_mentions) { create(:agent, groups: [mention_group], preferences: prefs_matrix_no_mentions) }
  1225. let(:ticket) { create(:ticket, group: mention_group, owner: user_no_mentions) }
  1226. it 'does inform mention user about the ticket update' do
  1227. create(:mention, mentionable: ticket, user: user_only_mentions)
  1228. create(:mention, mentionable: ticket, user: user_read_mentions)
  1229. create(:mention, mentionable: ticket, user: user_no_mentions)
  1230. perform_enqueued_jobs commit_transaction: true
  1231. check_notification do
  1232. ticket.update(priority: Ticket::Priority.find_by(name: '3 high'))
  1233. perform_enqueued_jobs commit_transaction: true
  1234. sent(
  1235. template: 'ticket_update',
  1236. user: user_no_mentions,
  1237. )
  1238. sent(
  1239. template: 'ticket_update',
  1240. user: user_read_mentions,
  1241. )
  1242. sent(
  1243. template: 'ticket_update',
  1244. user: user_only_mentions,
  1245. )
  1246. end
  1247. end
  1248. it 'does not inform mention user about the ticket update' do
  1249. ticket
  1250. perform_enqueued_jobs commit_transaction: true
  1251. check_notification do
  1252. ticket.update(priority: Ticket::Priority.find_by(name: '3 high'))
  1253. perform_enqueued_jobs commit_transaction: true
  1254. sent(
  1255. template: 'ticket_update',
  1256. user: user_no_mentions,
  1257. )
  1258. not_sent(
  1259. template: 'ticket_update',
  1260. user: user_read_mentions,
  1261. )
  1262. not_sent(
  1263. template: 'ticket_update',
  1264. user: user_only_mentions,
  1265. )
  1266. end
  1267. end
  1268. it 'does inform mention user about ticket creation' do
  1269. check_notification do
  1270. ticket = create(:ticket, owner: user_no_mentions, group: mention_group)
  1271. create(:mention, mentionable: ticket, user: user_read_mentions)
  1272. create(:mention, mentionable: ticket, user: user_only_mentions)
  1273. perform_enqueued_jobs commit_transaction: true
  1274. sent(
  1275. template: 'ticket_create',
  1276. user: user_no_mentions,
  1277. )
  1278. sent(
  1279. template: 'ticket_create',
  1280. user: user_read_mentions,
  1281. )
  1282. sent(
  1283. template: 'ticket_create',
  1284. user: user_only_mentions,
  1285. )
  1286. end
  1287. end
  1288. it 'does not inform mention user about ticket creation' do
  1289. check_notification do
  1290. create(:ticket, owner: user_no_mentions, group: mention_group)
  1291. perform_enqueued_jobs commit_transaction: true
  1292. sent(
  1293. template: 'ticket_create',
  1294. user: user_no_mentions,
  1295. )
  1296. not_sent(
  1297. template: 'ticket_create',
  1298. user: user_read_mentions,
  1299. )
  1300. not_sent(
  1301. template: 'ticket_create',
  1302. user: user_only_mentions,
  1303. )
  1304. end
  1305. end
  1306. it 'does not inform mention user about ticket creation because of no permissions' do
  1307. check_notification do
  1308. ticket = create(:ticket, group: no_access_group)
  1309. build(:mention, mentionable: ticket, user: user_read_mentions).save!(validate: false)
  1310. build(:mention, mentionable: ticket, user: user_only_mentions).save!(validate: false)
  1311. perform_enqueued_jobs commit_transaction: true
  1312. not_sent(
  1313. template: 'ticket_create',
  1314. user: user_read_mentions,
  1315. )
  1316. not_sent(
  1317. template: 'ticket_create',
  1318. user: user_only_mentions,
  1319. )
  1320. end
  1321. end
  1322. end
  1323. context 'selectors' do
  1324. let(:mention_group) { create(:group) }
  1325. let(:ticket_mentions) { create(:ticket, group: mention_group) }
  1326. let(:ticket_normal) { create(:ticket, group: mention_group) }
  1327. let(:user_mentions) { create(:agent, groups: [mention_group]) }
  1328. let(:user_mentions_2) { create(:agent, groups: [mention_group]) }
  1329. let(:user_no_mentions) { create(:agent, groups: [mention_group]) }
  1330. before do
  1331. described_class.destroy_all
  1332. ticket_normal
  1333. user_no_mentions
  1334. create(:mention, mentionable: ticket_mentions, user: user_mentions)
  1335. end
  1336. it 'pre condition is not_set' do
  1337. condition = {
  1338. 'ticket.mention_user_ids' => {
  1339. pre_condition: 'not_set',
  1340. operator: 'is',
  1341. },
  1342. }
  1343. expect(described_class.selectors(condition, limit: 100, access: 'full'))
  1344. .to contain_exactly(1, [ticket_normal])
  1345. end
  1346. it 'pre condition is not not_set' do
  1347. condition = {
  1348. 'ticket.mention_user_ids' => {
  1349. pre_condition: 'not_set',
  1350. operator: 'is not',
  1351. },
  1352. }
  1353. expect(described_class.selectors(condition, limit: 100, access: 'full'))
  1354. .to contain_exactly(1, [ticket_mentions])
  1355. end
  1356. it 'pre condition is current_user.id' do
  1357. condition = {
  1358. 'ticket.mention_user_ids' => {
  1359. pre_condition: 'current_user.id',
  1360. operator: 'is',
  1361. },
  1362. }
  1363. expect(described_class.selectors(condition, limit: 100, access: 'full', current_user: user_mentions))
  1364. .to contain_exactly(1, [ticket_mentions])
  1365. end
  1366. it 'pre condition is not current_user.id (one mention on one ticket)' do
  1367. condition = {
  1368. 'ticket.mention_user_ids' => {
  1369. pre_condition: 'current_user.id',
  1370. operator: 'is not',
  1371. },
  1372. }
  1373. expect(described_class.selectors(condition, limit: 100, access: 'full', current_user: user_mentions))
  1374. .to contain_exactly(1, [ticket_normal])
  1375. end
  1376. it 'pre condition is not current_user.id (multiple mentions on one ticket)' do
  1377. create(:mention, mentionable: ticket_mentions, user: user_mentions_2)
  1378. condition = {
  1379. 'ticket.mention_user_ids' => {
  1380. pre_condition: 'current_user.id',
  1381. operator: 'is not',
  1382. },
  1383. }
  1384. expect(described_class.selectors(condition, limit: 100, access: 'full', current_user: user_mentions))
  1385. .to contain_exactly(1, [ticket_normal])
  1386. end
  1387. it 'pre condition is specific' do
  1388. create(:mention, mentionable: ticket_mentions, user: user_mentions_2)
  1389. condition = {
  1390. 'ticket.mention_user_ids' => {
  1391. pre_condition: 'specific',
  1392. operator: 'is',
  1393. value: [user_mentions.id, user_mentions_2.id]
  1394. },
  1395. }
  1396. expect(described_class.selectors(condition, limit: 100, access: 'full'))
  1397. .to contain_exactly(1, [ticket_mentions].to_a)
  1398. end
  1399. it 'pre condition is not specific' do
  1400. condition = {
  1401. 'ticket.mention_user_ids' => {
  1402. pre_condition: 'specific',
  1403. operator: 'is not',
  1404. value: [user_mentions.id, user_mentions_2.id]
  1405. },
  1406. }
  1407. expect(described_class.selectors(condition, limit: 100, access: 'full'))
  1408. .to contain_exactly(1, [ticket_normal])
  1409. end
  1410. end
  1411. end
  1412. describe '.search_index_attribute_lookup_oversized?' do
  1413. subject!(:ticket) { create(:ticket) }
  1414. context 'when payload is ok' do
  1415. let(:current_payload_size) { 3.megabyte }
  1416. it 'return false' do
  1417. expect(ticket.send(:search_index_attribute_lookup_oversized?, current_payload_size)).to be false
  1418. end
  1419. end
  1420. context 'when payload is bigger' do
  1421. let(:current_payload_size) { 350.megabyte }
  1422. it 'return true' do
  1423. expect(ticket.send(:search_index_attribute_lookup_oversized?, current_payload_size)).to be true
  1424. end
  1425. end
  1426. end
  1427. describe '.search_index_attribute_lookup_file_oversized?' do
  1428. subject!(:store) do
  1429. create(:store,
  1430. object: 'SomeObject',
  1431. o_id: 1,
  1432. data: 'a' * ((1024**2) * 2.4), # with 2.4 mb
  1433. filename: 'test.TXT')
  1434. end
  1435. context 'when total payload is ok' do
  1436. let(:current_payload_size) { 200.megabyte }
  1437. it 'return false' do
  1438. expect(ticket.send(:search_index_attribute_lookup_file_oversized?, store, current_payload_size)).to be false
  1439. end
  1440. end
  1441. context 'when total payload is oversized' do
  1442. let(:current_payload_size) { 299.megabyte }
  1443. it 'return true' do
  1444. expect(ticket.send(:search_index_attribute_lookup_file_oversized?, store, current_payload_size)).to be true
  1445. end
  1446. end
  1447. end
  1448. describe '.search_index_attribute_lookup_file_ignored?' do
  1449. context 'when attachment is indexable' do
  1450. subject!(:store_with_indexable_extention) do
  1451. create(:store,
  1452. object: 'SomeObject',
  1453. o_id: 1,
  1454. data: 'some content',
  1455. filename: 'test.TXT')
  1456. end
  1457. it 'return false' do
  1458. expect(ticket.send(:search_index_attribute_lookup_file_ignored?, store_with_indexable_extention)).to be false
  1459. end
  1460. end
  1461. context 'when attachment is no indexable' do
  1462. subject!(:store_without_indexable_extention) do
  1463. create(:store,
  1464. object: 'SomeObject',
  1465. o_id: 1,
  1466. data: 'some content',
  1467. filename: 'test.BIN')
  1468. end
  1469. it 'return true' do
  1470. expect(ticket.send(:search_index_attribute_lookup_file_ignored?, store_without_indexable_extention)).to be true
  1471. end
  1472. end
  1473. end
  1474. describe '.search_index_attribute_lookup' do
  1475. subject!(:ticket) { create(:ticket) }
  1476. let(:search_index_attribute_lookup) do
  1477. article1 = create(:ticket_article, ticket: ticket)
  1478. create(:store,
  1479. object: 'Ticket::Article',
  1480. o_id: article1.id,
  1481. data: 'some content',
  1482. filename: 'some_file.bin',
  1483. preferences: {
  1484. 'Content-Type' => 'text/plain',
  1485. })
  1486. create(:store,
  1487. object: 'Ticket::Article',
  1488. o_id: article1.id,
  1489. data: 'a' * ((1024**2) * 2.4), # with 2.4 mb
  1490. filename: 'some_file.pdf',
  1491. preferences: {
  1492. 'Content-Type' => 'image/pdf',
  1493. })
  1494. create(:store,
  1495. object: 'Ticket::Article',
  1496. o_id: article1.id,
  1497. data: 'a' * ((1024**2) * 5.8), # with 5,8 mb
  1498. filename: 'some_file.txt',
  1499. preferences: {
  1500. 'Content-Type' => 'text/plain',
  1501. })
  1502. create(:ticket_article, ticket: ticket, body: 'a' * ((1024**2) * 1.2)) # body with 1,2 mb
  1503. create(:ticket_article, ticket: ticket)
  1504. ticket.search_index_attribute_lookup
  1505. end
  1506. context 'when es_attachment_max_size_in_mb takes all attachments' do
  1507. before { Setting.set('es_attachment_max_size_in_mb', 15) }
  1508. it 'verify count of articles' do
  1509. expect(search_index_attribute_lookup['article'].count).to eq 3
  1510. end
  1511. it 'verify count of attachments' do
  1512. expect(search_index_attribute_lookup['article'][0]['attachment'].count).to eq 2
  1513. end
  1514. it 'verify if pdf exists' do
  1515. expect(search_index_attribute_lookup['article'][0]['attachment'][0]['_name']).to eq 'some_file.pdf'
  1516. end
  1517. it 'verify if txt exists' do
  1518. expect(search_index_attribute_lookup['article'][0]['attachment'][1]['_name']).to eq 'some_file.txt'
  1519. end
  1520. end
  1521. context 'when es_attachment_max_size_in_mb takes only one attachment' do
  1522. before { Setting.set('es_attachment_max_size_in_mb', 4) }
  1523. it 'verify count of articles' do
  1524. expect(search_index_attribute_lookup['article'].count).to eq 3
  1525. end
  1526. it 'verify count of attachments' do
  1527. expect(search_index_attribute_lookup['article'][0]['attachment'].count).to eq 1
  1528. end
  1529. it 'verify if pdf exists' do
  1530. expect(search_index_attribute_lookup['article'][0]['attachment'][0]['_name']).to eq 'some_file.pdf'
  1531. end
  1532. end
  1533. context 'when es_attachment_max_size_in_mb takes no attachment' do
  1534. before { Setting.set('es_attachment_max_size_in_mb', 2) }
  1535. it 'verify count of articles' do
  1536. expect(search_index_attribute_lookup['article'].count).to eq 3
  1537. end
  1538. it 'verify count of attachments' do
  1539. expect(search_index_attribute_lookup['article'][0]['attachment'].count).to eq 0
  1540. end
  1541. end
  1542. context 'when es_total_max_size_in_mb takes no attachment and no oversized article' do
  1543. before { Setting.set('es_total_max_size_in_mb', 1) }
  1544. it 'verify count of articles' do
  1545. expect(search_index_attribute_lookup['article'].count).to eq 2
  1546. end
  1547. it 'verify count of attachments' do
  1548. expect(search_index_attribute_lookup['article'][0]['attachment'].count).to eq 0
  1549. end
  1550. end
  1551. end
  1552. describe '#reopen_after_certain_time?' do
  1553. context 'when groups.follow_up_possible is set to "new_ticket_after_certain_time"' do
  1554. let(:group) { create(:group, follow_up_possible: 'new_ticket_after_certain_time', reopen_time_in_days: 2) }
  1555. context 'when ticket is open' do
  1556. let(:ticket) { create(:ticket, group: group, state: Ticket::State.find_by(name: 'open')) }
  1557. it 'returns false' do
  1558. expect(ticket.reopen_after_certain_time?).to be false
  1559. end
  1560. end
  1561. context 'when ticket is closed' do
  1562. let(:ticket) { create(:ticket, group: group, state: Ticket::State.find_by(name: 'closed')) }
  1563. context 'when it is within configured time frame' do
  1564. it 'returns true' do
  1565. expect(ticket.reopen_after_certain_time?).to be true
  1566. end
  1567. end
  1568. context 'when it is outside configured time frame' do
  1569. before do
  1570. ticket
  1571. travel 3.days
  1572. end
  1573. it 'returns false' do
  1574. expect(ticket.reopen_after_certain_time?).to be false
  1575. end
  1576. end
  1577. end
  1578. context 'when reopen_time_in_days is not set' do
  1579. let(:group) { create(:group, follow_up_possible: 'new_ticket_after_certain_time', reopen_time_in_days: -1) }
  1580. it 'returns false' do
  1581. expect(ticket.reopen_after_certain_time?).to be false
  1582. end
  1583. end
  1584. end
  1585. end
  1586. describe '#get_references' do
  1587. let!(:ticket) { create(:ticket) }
  1588. let!(:articles) { create_list(:ticket_article, 10, ticket: ticket, reply_to: nil) }
  1589. before do
  1590. articles.each do |article|
  1591. article.update(message_id: SecureRandom.uuid)
  1592. end
  1593. end
  1594. it 'does return references' do
  1595. expect(ticket.get_references.count).to eq(10)
  1596. end
  1597. it 'does return references by limit' do
  1598. expect(ticket.get_references([], max_length: articles.last.message_id.length * 3).count).to eq(3)
  1599. end
  1600. it 'does return last 3 references by limit' do
  1601. expect(ticket.get_references([], max_length: articles.last.message_id.length * 3)).to eq(articles.map(&:message_id)[-3..])
  1602. end
  1603. it 'does ignore references' do
  1604. expect(ticket.get_references([articles.last.message_id])).not_to include(articles.last.message_id)
  1605. end
  1606. end
  1607. end