escalation_spec.rb 25 KB

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