ticket_spec.rb 70 KB

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