escalation_spec.rb 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699
  1. # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. require 'rails_helper'
  3. RSpec.describe Escalation do
  4. let(:instance) { described_class.new ticket, force: force }
  5. let(:instance_with_history) { described_class.new ticket_with_history, force: force }
  6. let(:instance_with_open) { described_class.new open_ticket_with_history, force: force }
  7. let(:ticket) { create(:ticket) }
  8. let(:force) { false }
  9. let(:calendar) { create(:calendar, :'24/7') }
  10. let(:sla_247) { create(:sla, :condition_blank, solution_time: 75, calendar: calendar) }
  11. let(:sla_247_response) { create(:sla, :condition_blank, first_response_time: 30, response_time: 45, solution_time: 75, calendar: calendar) }
  12. let(:sla_247_update) { create(:sla, :condition_blank, first_response_time: 30, update_time: 60, solution_time: 75, calendar: calendar) }
  13. let(:ticket_with_history) do
  14. freeze_time
  15. ticket = create(:ticket)
  16. ticket.update! state: Ticket::State.lookup(name: 'new')
  17. travel 1.hour
  18. ticket.update! state: Ticket::State.lookup(name: 'open')
  19. travel 30.minutes
  20. ticket.update! state: Ticket::State.lookup(name: 'pending close')
  21. travel 30.minutes
  22. ticket.update! state: Ticket::State.lookup(name: 'closed'), close_at: Time.current
  23. ticket
  24. end
  25. let(:open_ticket_with_history) do
  26. freeze_time
  27. article = create(:ticket_article, :inbound_email)
  28. travel 10.minutes
  29. article.ticket.update! state: Ticket::State.lookup(name: 'pending close')
  30. travel 10.minutes
  31. article.ticket.update! state: Ticket::State.lookup(name: 'open')
  32. article.ticket
  33. end
  34. describe '#preferences' do
  35. it { expect(instance.preferences).to be_a Escalation::TicketPreferences }
  36. end
  37. describe '#escalation_disabled?' do
  38. it 'true when ticket is not open' do
  39. ticket.update! state: Ticket::State.lookup(name: 'pending close')
  40. expect(instance).to be_escalation_disabled
  41. end
  42. it 'false when ticket is open' do
  43. expect(instance).not_to be_escalation_disabled
  44. end
  45. end
  46. describe '#calculatable?' do
  47. it 'false when ticket is not open' do
  48. ticket.update! state: Ticket::State.lookup(name: 'pending close')
  49. expect(instance).not_to be_calculatable
  50. end
  51. it 'true when ticket is open' do
  52. expect(instance).to be_calculatable
  53. end
  54. # https://github.com/zammad/zammad/issues/2579
  55. it 'true when ticket was just closed' do
  56. ticket
  57. travel 30.minutes
  58. without_update_escalation_information_callback { ticket.update close_at: Time.current, state: Ticket::State.lookup(name: 'closed') }
  59. expect(instance).to be_calculatable
  60. end
  61. it 'true when response to ticket comes while ticket has pending reminder' do
  62. ticket.update(state: Ticket::State.find_by(name: 'pending reminder'))
  63. without_update_escalation_information_callback { create(:'ticket/article', :outbound_email, ticket: ticket) }
  64. expect(instance).to be_calculatable
  65. end
  66. end
  67. describe '#calculate' do
  68. it 'works and updates' do
  69. ticket
  70. sla_247
  71. expect { instance.calculate }.to change(ticket, :has_changes_to_save?).to(true)
  72. end
  73. it 'exit early when escalation is disabled' do
  74. allow(instance).to receive(:escalation_disabled?).and_return(true)
  75. allow(instance).to receive(:calendar) # next method called after checking escalation state
  76. instance.calculate
  77. expect(instance).not_to have_received(:calendar)
  78. end
  79. it 'recalculate when escalation is disabled but it is forced' do
  80. instance_forced = described_class.new ticket, force: true
  81. allow(instance_forced).to receive(:escalation_disabled?).and_return(true)
  82. allow(instance_forced).to receive(:calendar) # next method called after checking escalation state
  83. instance_forced.calculate
  84. expect(instance_forced).to have_received(:calendar)
  85. end
  86. it 'no calendar is early exit' do
  87. allow(instance).to receive(:calendar).and_return(nil)
  88. allow(instance.preferences).to receive(:any_changes?) # next method after the check
  89. instance.calculate
  90. expect(instance.preferences).not_to have_received(:any_changes?)
  91. end
  92. it 'no calendar resets' do
  93. allow(instance).to receive_messages(calendar: nil, forced?: true)
  94. allow(instance).to receive(:calculate_no_calendar)
  95. instance.calculate
  96. expect(instance).to have_received(:calculate_no_calendar)
  97. end
  98. context 'with SLA 24/7' do
  99. before { sla_247 }
  100. it 'forces recalculation when SLA touched' do
  101. allow(instance.preferences).to receive(:sla_changed?).and_return(true)
  102. allow(instance).to receive(:force!)
  103. instance.calculate
  104. expect(instance).to have_received(:force!)
  105. end
  106. it 'calculates when ticket was touched in a related manner' do
  107. allow(instance.preferences).to receive(:any_changes?).and_return(true)
  108. allow(instance).to receive(:update_escalations)
  109. instance.calculate
  110. expect(instance).to have_received(:update_escalations)
  111. end
  112. it 'skips calculating escalation times when ticket was not touched in a related manner' do
  113. allow(instance.preferences).to receive(:any_changes?).and_return(false)
  114. allow(instance).to receive(:update_escalations)
  115. instance.calculate
  116. expect(instance).not_to have_received(:update_escalations)
  117. end
  118. it 'calculates statistics when ticket was touched in a related manner' do
  119. allow(instance.preferences).to receive(:any_changes?).and_return(true)
  120. allow(instance).to receive(:update_statistics)
  121. instance.calculate
  122. expect(instance).to have_received(:update_statistics)
  123. end
  124. it 'skips calculating statistics when ticket was not touched in a related manner' do
  125. allow(instance.preferences).to receive(:any_changes?).and_return(false)
  126. allow(instance).to receive(:update_statistics)
  127. instance.calculate
  128. expect(instance).not_to have_received(:update_statistics)
  129. end
  130. it 'setting #first_response_at does not nullify other escalations' do
  131. ticket.update! first_response_at: 30.minutes.from_now
  132. expect(ticket.reload.close_escalation_at).not_to be_nil
  133. end
  134. it 'setting ticket to non-escalatable state clears #escalation_at' do
  135. ticket.update! state: Ticket::State.lookup(name: 'closed')
  136. expect(ticket.escalation_at).to be_nil
  137. end
  138. # https://github.com/zammad/zammad/issues/2579
  139. it 'calculates closing statistics on closing ticket' do
  140. ticket
  141. travel 30.minutes
  142. without_update_escalation_information_callback { ticket.update close_at: Time.current, state: Ticket::State.lookup(name: 'closed') }
  143. expect { instance.calculate }.to change(ticket, :close_in_min).from(nil)
  144. end
  145. end
  146. end
  147. describe '#force!' do
  148. it 'sets forced? to true' do
  149. expect { instance.send(:force!) }.to change(instance, :forced?).from(false).to(true)
  150. end
  151. end
  152. describe 'calculate_not_calculatable' do
  153. it 'sets escalation dates to nil' do
  154. sla_247
  155. open_ticket_with_history
  156. instance = described_class.new open_ticket_with_history
  157. instance.calculate_not_calculatable
  158. expect(open_ticket_with_history).to have_attributes(escalation_at: nil, first_response_escalation_at: nil, update_escalation_at: nil, close_escalation_at: nil)
  159. end
  160. end
  161. describe '#sla' do
  162. it 'returns SLA when it exists' do
  163. sla_247
  164. expect(instance.sla).to be_a(Sla)
  165. end
  166. it 'returns nil when no SLA' do
  167. expect(instance.sla).to be_nil
  168. end
  169. end
  170. describe '#calendar' do
  171. it 'returns calendar when it exists' do
  172. sla_247
  173. expect(instance.calendar).to be_a(Calendar)
  174. end
  175. it 'returns nil when no calendar' do
  176. expect(instance.calendar).to be_nil
  177. end
  178. end
  179. describe '#forced?' do
  180. it 'true when given true' do
  181. instance = described_class.new ticket, force: true
  182. expect(instance).to be_forced
  183. end
  184. it 'false when given false' do
  185. instance = described_class.new ticket, force: false
  186. expect(instance).not_to be_forced
  187. end
  188. it 'false when given nil' do
  189. instance = described_class.new ticket, force: nil
  190. expect(instance).not_to be_forced
  191. end
  192. end
  193. describe '#update_escalations' do
  194. it 'sets escalation times' do
  195. instance = described_class.new open_ticket_with_history
  196. sla_247
  197. expect { instance.update_escalations }
  198. .to change(open_ticket_with_history, :escalation_at).from(nil)
  199. end
  200. # https://github.com/zammad/zammad/issues/3140
  201. it 'agent follow up does not set #update_escalation_at' do
  202. sla_247
  203. ticket
  204. create(:ticket_article, :outbound_email, ticket: ticket)
  205. expect(ticket.reload.update_escalation_at).to be_nil
  206. end
  207. # https://github.com/zammad/zammad/issues/3140
  208. it 'customer contact sets #update_escalation_at' do
  209. sla_247_response
  210. ticket
  211. create(:ticket_article, :inbound_email, ticket: ticket)
  212. expect(ticket.reload.update_escalation_at).to be_a(Time)
  213. end
  214. context 'with ticket with sla and customer enquiry' do
  215. before do
  216. sla_247_response
  217. ticket
  218. travel 10.minutes
  219. create(:ticket_article, :inbound_email, ticket: ticket)
  220. travel 10.minutes
  221. end
  222. # https://github.com/zammad/zammad/issues/3140
  223. it 'agent response clears #update_escalation_at' do
  224. expect { create(:ticket_article, :outbound_email, ticket: ticket) }
  225. .to change { ticket.reload.update_escalation_at }.to(nil)
  226. end
  227. # https://github.com/zammad/zammad/issues/3140
  228. it 'repeated customer requests do not #update_escalation_at' do
  229. expect { create(:ticket_article, :inbound_email, ticket: ticket) }
  230. .not_to change { ticket.reload.update_escalation_at }
  231. end
  232. end
  233. end
  234. describe '#escalation_first_response' do
  235. let(:force) { true } # initial calculation
  236. it 'returns attribute' do
  237. sla_247_response
  238. allow(instance_with_history).to receive(:escalation_disabled?).and_return(false)
  239. result = instance_with_history.send(:escalation_first_response)
  240. expect(result).to include first_response_escalation_at: 90.minutes.ago
  241. end
  242. it 'returns nil when no sla#first_response_time' do
  243. sla_247_response.update! first_response_time: nil
  244. allow(instance_with_history).to receive(:escalation_disabled?).and_return(false)
  245. result = instance_with_history.send(:escalation_first_response)
  246. expect(result).to include(first_response_escalation_at: nil)
  247. end
  248. end
  249. describe '#escalation_update_reset' do
  250. it 'resets to nil when no sla#response_time and sla#update_time' do
  251. sla_247
  252. allow(instance_with_history).to receive(:escalation_disabled?).and_return(false)
  253. result = instance_with_history.send(:escalation_update_reset)
  254. expect(result).to include(update_escalation_at: nil)
  255. end
  256. it 'returns nil when no sla#response_time' do
  257. sla_247_update
  258. allow(instance_with_history).to receive(:escalation_disabled?).and_return(false)
  259. result = instance_with_history.send(:escalation_update_reset)
  260. expect(result).to be_nil
  261. end
  262. it 'returns nil when no sla#update_time' do
  263. sla_247_response
  264. allow(instance_with_history).to receive(:escalation_disabled?).and_return(false)
  265. result = instance_with_history.send(:escalation_update_reset)
  266. expect(result).to be_nil
  267. end
  268. end
  269. describe '#escalation_response' do
  270. it 'returns attribute' do
  271. sla_247_response
  272. ticket_with_history.last_contact_customer_at = 2.hours.ago
  273. allow(instance_with_history).to receive(:escalation_disabled?).and_return(false)
  274. result = instance_with_history.send(:escalation_response)
  275. expect(result).to include update_escalation_at: 75.minutes.ago
  276. end
  277. it 'returns nil when no sla#response_time' do
  278. sla_247
  279. allow(instance_with_history).to receive(:escalation_disabled?).and_return(false)
  280. result = instance_with_history.send(:escalation_response)
  281. expect(result).to be_nil
  282. end
  283. it 'response time is calculated when waiting for the first response with update-only SLA' do
  284. sla_247_response.update! first_response_time: nil
  285. ticket_with_history.last_contact_customer_at = 2.hours.ago
  286. allow(instance_with_history).to receive(:escalation_disabled?).and_return(false)
  287. result = instance_with_history.send(:escalation_response)
  288. expect(result).to include update_escalation_at: 75.minutes.ago
  289. end
  290. end
  291. describe '#escalation_update' do
  292. context 'when has open ticket with history' do
  293. before do
  294. sla_247_update
  295. open_ticket_with_history
  296. allow(instance_with_open).to receive(:escalation_disabled?).and_return(false)
  297. end
  298. it 'update time is calculated before first agent response' do
  299. result = instance_with_open.send(:escalation_update)
  300. expect(result).to include update_escalation_at: 50.minutes.from_now
  301. end
  302. it 'update time is calculated after agent response' do
  303. create(:ticket_article, :outbound_email, ticket: open_ticket_with_history)
  304. result = instance_with_open.send(:escalation_update)
  305. expect(result).to include update_escalation_at: 60.minutes.from_now
  306. end
  307. context 'when agent responds' do
  308. before do
  309. create(:ticket_article, :outbound_email, ticket: open_ticket_with_history)
  310. travel 30.minutes
  311. end
  312. it 'update time is calculated after 2nd customer enquiry' do
  313. create(:ticket_article, :inbound_email, ticket: open_ticket_with_history)
  314. result = instance_with_open.send(:escalation_update)
  315. expect(result).to include update_escalation_at: 30.minutes.from_now
  316. end
  317. it 'update time is calculated after 2nd agent response interrupted by customer' do
  318. create(:ticket_article, :inbound_email, ticket: open_ticket_with_history)
  319. travel 30.minutes
  320. create(:ticket_article, :outbound_email, ticket: open_ticket_with_history)
  321. result = instance_with_open.send(:escalation_update)
  322. expect(result).to include update_escalation_at: 60.minutes.from_now
  323. end
  324. it 'update time is calculated after 2nd agent response in a row' do
  325. create(:ticket_article, :outbound_email, ticket: open_ticket_with_history)
  326. result = instance_with_open.send(:escalation_update)
  327. expect(result).to include update_escalation_at: 60.minutes.from_now
  328. end
  329. end
  330. end
  331. it 'returns nil when no sla#update_time' do
  332. sla_247
  333. allow(instance_with_open).to receive(:escalation_disabled?).and_return(false)
  334. result = instance_with_open.send(:escalation_update)
  335. expect(result).to be_nil
  336. end
  337. end
  338. describe '#escalation_close' do
  339. it 'returns attribute' do
  340. sla_247
  341. ticket_with_history.update! state: Ticket::State.lookup(name: 'open'), close_at: nil
  342. allow(instance_with_history).to receive(:escalation_disabled?).and_return(false)
  343. result = instance_with_history.send(:escalation_close)
  344. expect(result).to include close_escalation_at: 45.minutes.ago
  345. end
  346. it 'returns nil when no sla#solution_time' do
  347. sla_247.update! solution_time: nil
  348. allow(instance_with_history).to receive(:escalation_disabled?).and_return(false)
  349. result = instance_with_history.send(:escalation_close)
  350. expect(result).to include(close_escalation_at: nil)
  351. end
  352. end
  353. describe '#calculate_time' do
  354. before do
  355. sla_247
  356. start
  357. end
  358. let(:start) { 75.minutes.from_now.change(sec: 0) }
  359. it 'calculates target time that is given working minutes after start time' do
  360. expect(instance_with_history.send(:calculate_time, start, 30)).to eq(start + 1.hour)
  361. end
  362. it 'returns nil when given 0 span' do
  363. expect(instance_with_history.send(:calculate_time, start, 0)).to be_nil
  364. end
  365. it 'returns nil when given no span' do
  366. expect(instance_with_history.send(:calculate_time, start, nil)).to be_nil
  367. end
  368. end
  369. describe '#calculate_next_escalation' do
  370. it 'nil when escalation is disabled' do
  371. ticket.update! state: Ticket::State.lookup(name: 'closed')
  372. expect(instance.send(:calculate_next_escalation)).to be_nil
  373. end
  374. it 'first_response_escalation_at when earliest' do
  375. ticket.update! first_response_escalation_at: 1.hour.from_now, update_escalation_at: 2.hours.from_now, close_escalation_at: 3.hours.from_now
  376. expect(instance.send(:calculate_next_escalation)).to eq ticket.first_response_escalation_at
  377. end
  378. it 'update_escalation_at when earliest' do
  379. ticket.update! first_response_escalation_at: 2.hours.from_now, update_escalation_at: 1.hour.from_now, close_escalation_at: 3.hours.from_now
  380. expect(instance.send(:calculate_next_escalation)).to eq ticket.update_escalation_at
  381. end
  382. it 'close_escalation_at when earliest' do
  383. ticket.update! first_response_escalation_at: 2.hours.from_now, update_escalation_at: 1.hour.from_now, close_escalation_at: 30.minutes.from_now
  384. expect(instance.send(:calculate_next_escalation)).to eq ticket.close_escalation_at
  385. end
  386. it 'works when one of escalation times is not present' do
  387. ticket.update! first_response_escalation_at: 1.hour.from_now, update_escalation_at: nil, close_escalation_at: nil
  388. expect { instance.send(:calculate_next_escalation) }.not_to raise_error
  389. end
  390. end
  391. describe '#statistics_first_response' do
  392. it 'calculates statistics' do
  393. sla_247_response
  394. ticket_with_history.first_response_at = 45.minutes.ago
  395. instance_with_history.force!
  396. result = instance_with_history.send(:statistics_first_response)
  397. expect(result).to include(first_response_in_min: 75, first_response_diff_in_min: -45)
  398. end
  399. it 'does not touch statistics when sla time is nil' do
  400. sla_247_response.update! first_response_time: nil
  401. ticket_with_history.first_response_at = 45.minutes.ago
  402. instance_with_history.force!
  403. result = instance_with_history.send(:statistics_first_response)
  404. expect(result).to be_nil
  405. end
  406. end
  407. describe '#statistics_response' do
  408. before do
  409. sla_247_response
  410. freeze_time
  411. end
  412. it 'calculates statistics' do
  413. ticket_with_history.last_contact_customer_at = 61.minutes.ago
  414. ticket_with_history.last_contact_agent_at = 60.minutes.ago
  415. result = instance_with_history.send(:statistics_response)
  416. expect(result).to include(update_in_min: 1, update_diff_in_min: 44)
  417. end
  418. it 'does not calculate statistics when customer respose is last' do
  419. ticket_with_history.last_contact_customer_at = 59.minutes.ago
  420. ticket_with_history.last_contact_agent_at = 60.minutes.ago
  421. result = instance_with_history.send(:statistics_response)
  422. expect(result).to be_nil
  423. end
  424. it 'does not calculate statistics when only customer enquiry present' do
  425. create(:ticket_article, :inbound_email, ticket: ticket)
  426. result = instance.send(:statistics_response)
  427. expect(result).to be_nil
  428. end
  429. it 'calculates update statistics of last exchange' do
  430. create(:ticket_article, :inbound_email, ticket: ticket)
  431. travel 10.minutes
  432. create(:ticket_article, :outbound_email, ticket: ticket)
  433. instance.force!
  434. expect(instance.send(:statistics_response)).to include(update_in_min: 10, update_diff_in_min: 35)
  435. end
  436. context 'with multiple exchanges and later one being quicker' do
  437. before do
  438. create(:ticket_article, :inbound_email, ticket: ticket)
  439. travel 10.minutes
  440. create(:ticket_article, :outbound_email, ticket: ticket)
  441. travel 10.minutes
  442. create(:ticket_article, :inbound_email, ticket: ticket)
  443. travel 5.minutes
  444. create(:ticket_article, :outbound_email, ticket: ticket)
  445. end
  446. it 'keeps statistics of longest exchange' do
  447. expect(ticket.reload).to have_attributes(update_in_min: 10, update_diff_in_min: 35)
  448. end
  449. end
  450. it 'does not touch statistics when sla time is nil' do
  451. sla_247.update! update_time: nil
  452. ticket_with_history.last_contact_customer_at = 60.minutes.ago
  453. instance_with_history.force!
  454. result = instance_with_history.send(:statistics_update)
  455. expect(result).to be_nil
  456. end
  457. it 'does not touch statistics when last update is nil' do
  458. ticket_with_history.assign_attributes last_contact_agent_at: nil, last_contact_customer_at: nil
  459. instance_with_history.force!
  460. result = instance_with_history.send(:statistics_update)
  461. expect(result).to be_nil
  462. end
  463. end
  464. describe '#statistics_update' do
  465. before do
  466. sla_247_update
  467. freeze_time
  468. end
  469. it 'does not calculate statistics when only customer enquiry present' do
  470. create(:ticket_article, :inbound_email, ticket: ticket)
  471. result = instance.send(:statistics_update)
  472. expect(result).to be_nil
  473. end
  474. context 'when agent responds after 20 minutes' do
  475. before do
  476. ticket
  477. travel 20.minutes
  478. create(:ticket_article, :outbound_email, ticket: ticket)
  479. end
  480. it 'does not touch statistics when customer response is most recent' do
  481. travel 30.minutes
  482. create(:ticket_article, :inbound_email, ticket: ticket)
  483. result = instance.send(:statistics_update)
  484. expect(result).to include(update_diff_in_min: 40, update_in_min: 20)
  485. end
  486. it 'calculates statistics when only agent update present' do
  487. result = instance.send(:statistics_update)
  488. expect(result).to include(update_diff_in_min: 40, update_in_min: 20)
  489. end
  490. it 'calculates statistics when multiple agent updates present' do
  491. travel 30.minutes
  492. create(:ticket_article, :outbound_email, ticket: ticket)
  493. result = instance.send(:statistics_update)
  494. expect(result).to include(update_diff_in_min: 30, update_in_min: 30)
  495. end
  496. context 'when customer responds' do
  497. before do
  498. travel 10.minutes
  499. create(:ticket_article, :inbound_email, ticket: ticket)
  500. end
  501. it 'calculates statistics when multiple agent updates intercepted by customer' do
  502. travel 35.minutes
  503. create(:ticket_article, :outbound_email, ticket: ticket)
  504. result = instance.send(:statistics_update)
  505. expect(result).to include(update_diff_in_min: 15, update_in_min: 45)
  506. end
  507. end
  508. end
  509. context 'with multiple exchanges and later one being quicker' do
  510. before do
  511. travel 10.minutes
  512. create(:ticket_article, :outbound_email, ticket: ticket)
  513. travel 5.minutes
  514. create(:ticket_article, :outbound_email, ticket: ticket)
  515. end
  516. it 'keeps statistics of longest exchange' do
  517. expect(ticket.reload).to have_attributes(update_in_min: 5, update_diff_in_min: 55)
  518. end
  519. end
  520. it 'does not touch statistics when sla time is nil' do
  521. sla_247.update! update_time: nil
  522. ticket_with_history.last_contact_customer_at = 60.minutes.ago
  523. instance_with_history.force!
  524. result = instance_with_history.send(:statistics_update)
  525. expect(result).to be_nil
  526. end
  527. it 'does not touch statistics when last update is nil' do
  528. ticket_with_history.assign_attributes last_contact_agent_at: nil, last_contact_customer_at: nil
  529. instance_with_history.force!
  530. result = instance_with_history.send(:statistics_update)
  531. expect(result).to be_nil
  532. end
  533. end
  534. describe '#statistics_close' do
  535. it 'calculates statistics' do
  536. sla_247
  537. ticket_with_history.close_at = 50.minutes.ago
  538. instance_with_history.force!
  539. result = instance_with_history.send(:statistics_close)
  540. expect(result).to include(close_in_min: 70, close_diff_in_min: 5)
  541. end
  542. it 'does not touch statistics when sla time is nil' do
  543. sla_247.update! solution_time: nil
  544. ticket_with_history.close_at = 50.minutes.ago
  545. instance_with_history.force!
  546. result = instance_with_history.send(:statistics_close)
  547. expect(result).to be_nil
  548. end
  549. end
  550. describe '#calculate_minutes' do
  551. it 'calculates working minutes up to given time' do
  552. sla_247
  553. expect(instance_with_history.send(:calculate_minutes, ticket_with_history.created_at, 90.minutes.ago)).to be 30
  554. end
  555. it 'returns nil when given nil' do
  556. sla_247
  557. expect(instance.send(:calculate_minutes, ticket.created_at, nil)).to be_nil
  558. end
  559. end
  560. it 'switching state pushes escalation date' do
  561. sla_247
  562. open_ticket_with_history.reload
  563. expect(open_ticket_with_history.escalation_at).to eq open_ticket_with_history.created_at + 85.minutes
  564. end
  565. def without_update_escalation_information_callback(&)
  566. Ticket.without_callback(:commit, :after, :update_escalation_information, &)
  567. end
  568. end