ticket_spec.rb 70 KB

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