perform_changes_spec.rb 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804
  1. # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. require 'rails_helper'
  3. require 'models/concerns/can_perform_changes_examples'
  4. RSpec.describe 'Ticket::PerformChanges', :aggregate_failures do
  5. subject(:object) { create(:ticket, group: group, owner: create(:agent, groups: [group])) }
  6. let(:group) { create(:group) }
  7. let(:performable) do
  8. create(:trigger, perform: perform, activator: 'action', execution_condition_mode: 'always', condition: { 'ticket.state_id'=>{ 'operator' => 'is', 'value' => Ticket::State.pluck(:id) } })
  9. end
  10. include_examples 'CanPerformChanges', object_name: 'Ticket'
  11. context 'when invalid data is given' do
  12. context 'with not existing attribute' do
  13. let(:perform) do
  14. {
  15. 'ticket.foobar' => {
  16. 'value' => 'dummy',
  17. }
  18. }
  19. end
  20. it 'raises an error' do
  21. expect { object.perform_changes(performable, 'trigger', object, User.first) }
  22. .to raise_error(RuntimeError, 'The given trigger contains invalid attributes, stopping!')
  23. end
  24. end
  25. context 'with invalid action in "perform" hash' do
  26. let(:perform) do
  27. {
  28. 'dummy' => {
  29. 'value' => 'delete',
  30. }
  31. }
  32. end
  33. it 'raises an error' do
  34. expect { object.perform_changes(performable, 'trigger', object, User.first) }
  35. .to raise_error(RuntimeError, 'The given trigger contains no valid actions, stopping!')
  36. end
  37. end
  38. end
  39. # Regression test for https://github.com/zammad/zammad/issues/2001
  40. describe 'argument handling' do
  41. let(:perform) do
  42. {
  43. 'notification.email' => {
  44. body: "Hello \#{ticket.customer.firstname} \#{ticket.customer.lastname},",
  45. recipient: %w[article_last_sender ticket_owner ticket_customer ticket_agents],
  46. subject: "Autoclose (\#{ticket.title})"
  47. }
  48. }
  49. end
  50. it 'does not mutate contents of "perform" hash' do
  51. expect { object.perform_changes(performable, 'trigger', {}, 1) }
  52. .not_to change { perform }
  53. end
  54. end
  55. context 'with "ticket.state_id" key in "perform" hash' do
  56. let(:perform) do
  57. {
  58. 'ticket.state_id' => {
  59. 'value' => Ticket::State.lookup(name: 'closed').id
  60. }
  61. }
  62. end
  63. it 'changes #state to specified value' do
  64. expect { object.perform_changes(performable, 'trigger', object, User.first) }
  65. .to change { object.reload.state.name }.to('closed')
  66. end
  67. end
  68. # Test for backwards compatibility after PR https://github.com/zammad/zammad/pull/2862
  69. context 'with "pending_time" => { "value": DATE } in "perform" hash' do
  70. let(:perform) do
  71. {
  72. 'ticket.state_id' => {
  73. 'value' => Ticket::State.lookup(name: 'pending reminder').id.to_s
  74. },
  75. 'ticket.pending_time' => {
  76. 'value' => timestamp,
  77. },
  78. }
  79. end
  80. let(:timestamp) { Time.zone.now }
  81. it 'changes pending date to given date' do
  82. freeze_time do
  83. expect { object.perform_changes(performable, 'trigger', object, User.first) }
  84. .to change(object, :pending_time)
  85. .to timestamp.change(sec: 0)
  86. end
  87. end
  88. end
  89. # Test for PR https://github.com/zammad/zammad/pull/2862
  90. context 'with "pending_time" => { "operator": "relative" } in "perform" hash' do
  91. shared_examples 'verify' do
  92. it 'verify relative pending time rule' do
  93. freeze_time do
  94. target_time = relative_value
  95. .send(relative_range)
  96. .from_now
  97. .change(sec: 0)
  98. expect { object.perform_changes(performable, 'trigger', object, User.first) }
  99. .to change(object, :pending_time)
  100. .to target_time
  101. end
  102. end
  103. end
  104. let(:perform) do
  105. {
  106. 'ticket.state_id' => {
  107. 'value' => Ticket::State.lookup(name: 'pending reminder').id.to_s
  108. },
  109. 'ticket.pending_time' => {
  110. 'operator' => 'relative',
  111. 'value' => relative_value,
  112. 'range' => relative_range_config
  113. },
  114. }
  115. end
  116. let(:relative_range_config) { relative_range.to_s.singularize }
  117. context 'when value in days' do
  118. let(:relative_value) { 2 }
  119. let(:relative_range) { :days }
  120. include_examples 'verify'
  121. end
  122. context 'when value in minutes' do
  123. let(:relative_value) { 60 }
  124. let(:relative_range) { :minutes }
  125. include_examples 'verify'
  126. end
  127. context 'when value in weeks' do
  128. let(:relative_value) { 2 }
  129. let(:relative_range) { :weeks }
  130. include_examples 'verify'
  131. end
  132. end
  133. context 'with tags in "perform" hash' do
  134. let(:user) { create(:agent, groups: [group]) }
  135. let(:perform) do
  136. {
  137. 'ticket.tags' => { 'operator' => tag_operator, 'value' => 'tag1, tag2' }
  138. }
  139. end
  140. context 'with add' do
  141. let(:tag_operator) { 'add' }
  142. it 'adds the tags' do
  143. expect { object.perform_changes(performable, 'trigger', object, user.id) }
  144. .to change { object.reload.tag_list }.to(%w[tag1 tag2])
  145. end
  146. end
  147. context 'with remove' do
  148. let(:tag_operator) { 'remove' }
  149. before do
  150. %w[tag1 tag2].each { |tag| object.tag_add(tag, 1) }
  151. end
  152. it 'removes the tags' do
  153. expect { object.perform_changes(performable, 'trigger', object, user.id) }
  154. .to change { object.reload.tag_list }.to([])
  155. end
  156. end
  157. end
  158. context 'with "pre_condition" in "perform" hash' do
  159. let(:user) { create(:agent, groups: [group]) }
  160. let(:perform) do
  161. {
  162. 'ticket.owner_id' => {
  163. 'pre_condition' => pre_condition,
  164. 'value' => value,
  165. 'value_completion' => '',
  166. }
  167. }
  168. end
  169. context 'with current_user.id' do
  170. let(:pre_condition) { 'current_user.id' }
  171. let(:value) { '' }
  172. it 'changes to specified value' do
  173. expect { object.perform_changes(performable, 'trigger', object, user.id) }
  174. .to change { object.reload.owner.id }.to(user.id)
  175. end
  176. end
  177. context 'with specific user' do
  178. let(:another_user) { create(:agent, groups: [group]) }
  179. let(:pre_condition) { 'specific' }
  180. let(:value) { another_user.id }
  181. it 'changes to specified value' do
  182. expect { object.perform_changes(performable, 'trigger', object, user.id) }
  183. .to change { object.reload.owner.id }.to(another_user.id)
  184. end
  185. end
  186. context 'with current_user.id, but missing user' do
  187. let(:pre_condition) { 'current_user.id' }
  188. let(:value) { '' }
  189. it 'raises an error' do
  190. expect { object.perform_changes(performable, 'trigger', object, nil) }
  191. .to raise_error(RuntimeError, "The required parameter 'user_id' is missing.")
  192. end
  193. end
  194. context 'with not_set' do
  195. let(:pre_condition) { 'not_set' }
  196. let(:value) { '' }
  197. it 'changes to user with id 1' do
  198. expect { object.perform_changes(performable, 'trigger', object, user.id) }
  199. .to change { object.reload.owner.id }.to(1)
  200. end
  201. end
  202. end
  203. context 'with "ticket.action" => { "value" => "delete" } in "perform" hash' do
  204. let(:perform) do
  205. {
  206. 'ticket.state_id' => { 'value' => Ticket::State.lookup(name: 'closed').id.to_s },
  207. 'ticket.action' => { 'value' => 'delete' },
  208. }
  209. end
  210. it 'performs a ticket deletion on a ticket' do
  211. expect { object.perform_changes(performable, 'trigger', object, User.first) }
  212. .to change(object, :destroyed?).to(true)
  213. end
  214. end
  215. context 'with a "notification.email" trigger' do
  216. # Regression test for https://github.com/zammad/zammad/issues/1543
  217. #
  218. # If a new article fires an email notification trigger,
  219. # and then another article is added to the same ticket
  220. # before that trigger is performed,
  221. # the email template's 'article' var should refer to the originating article,
  222. # not the newest one.
  223. #
  224. # (This occurs whenever one action fires multiple email notification triggers.)
  225. context 'when two articles are created before the trigger fires once (race condition)' do
  226. let!(:article) { create(:ticket_article, ticket: object) }
  227. let!(:new_article) { create(:ticket_article, ticket: object) }
  228. let(:trigger) do
  229. build(:trigger,
  230. perform: {
  231. 'notification.email' => {
  232. body: 'Sample notification',
  233. recipient: 'ticket_customer',
  234. subject: 'Sample subject'
  235. }
  236. })
  237. end
  238. let(:objects) do
  239. last_article = nil
  240. last_internal_article = nil
  241. last_external_article = nil
  242. all_articles = object.articles
  243. if article.nil?
  244. last_article = all_articles.last
  245. last_internal_article = all_articles.reverse.find(&:internal?)
  246. last_external_article = all_articles.reverse.find { |a| !a.internal? }
  247. else
  248. last_article = article
  249. last_internal_article = article.internal? ? article : all_articles.reverse.find(&:internal?)
  250. last_external_article = article.internal? ? all_articles.reverse.find { |a| !a.internal? } : article
  251. end
  252. {
  253. ticket: object,
  254. article: last_article,
  255. last_article: last_article,
  256. last_internal_article: last_internal_article,
  257. last_external_article: last_external_article,
  258. created_article: article,
  259. created_internal_article: article&.internal? ? article : nil,
  260. created_external_article: article&.internal? ? nil : article,
  261. }
  262. end
  263. # required by Ticket#perform_changes for email notifications
  264. before do
  265. allow(NotificationFactory::Mailer).to receive(:template).and_call_original
  266. article.ticket.group.update(email_address: create(:email_address))
  267. end
  268. it 'passes the first article to NotificationFactory::Mailer' do
  269. object.perform_changes(trigger, 'trigger', { article_id: article.id }, 1)
  270. expect(NotificationFactory::Mailer)
  271. .to have_received(:template)
  272. .with(hash_including(objects: objects))
  273. .at_least(:once)
  274. expect(NotificationFactory::Mailer)
  275. .not_to have_received(:template)
  276. .with(hash_including(objects: { ticket: object, article: new_article }))
  277. end
  278. end
  279. context 'when dispatching email through an inactive channel' do
  280. let!(:article) { create(:ticket_article, ticket: object) }
  281. let(:trigger) do
  282. build(:trigger,
  283. perform: {
  284. 'notification.email' => {
  285. body: 'Sample notification',
  286. recipient: 'ticket_customer',
  287. subject: 'Sample subject'
  288. }
  289. })
  290. end
  291. # required by Ticket#perform_changes for email notifications
  292. before do
  293. allow(NotificationFactory::Mailer).to receive(:template).and_call_original
  294. allow(Rails.logger).to receive(:info)
  295. article.ticket.group.update(email_address: create(:email_address, channel: create(:channel, active: false)))
  296. end
  297. it 'does not pass the article to NotificationFactory::Mailer' do
  298. object.perform_changes(trigger, 'trigger', { article_id: article.id }, 1)
  299. expect(Rails.logger).to have_received(:info).with(match(%r{because the channel .* is not active}))
  300. # no specific email content awaiting needed, since we do not expect to receive any mail (what ever it is) at the point
  301. expect(NotificationFactory::Mailer).not_to have_received(:template)
  302. end
  303. end
  304. end
  305. context 'with a notification trigger' do
  306. # https://github.com/zammad/zammad/issues/2782
  307. #
  308. # Notification triggers should log notification as private or public
  309. # according to given configuration
  310. let(:user) { create(:admin, mobile: '+37061010000') }
  311. let(:perform) do
  312. {
  313. notification_key => {
  314. body: 'Old programmers never die. They just branch to a new address.',
  315. recipient: 'ticket_agents',
  316. subject: 'Old programmers never die. They just branch to a new address.'
  317. }
  318. }.deep_merge(additional_options).deep_stringify_keys
  319. end
  320. let(:notification_key) { "notification.#{notification_type}" }
  321. let!(:ticket_article) { create(:ticket_article, ticket: object) }
  322. let(:item) do
  323. {
  324. object: 'Ticket',
  325. object_id: object.id,
  326. user_id: user.id,
  327. type: 'update',
  328. article_id: ticket_article.id
  329. }
  330. end
  331. before { object.group.users << user }
  332. shared_examples 'verify log visibility status' do
  333. shared_examples 'notification trigger' do
  334. it 'adds Ticket::Article' do
  335. expect { object.perform_changes(performable, 'trigger', object, user) }
  336. .to change { object.articles.count }.by(1)
  337. end
  338. it 'new Ticket::Article visibility reflects setting' do
  339. object.perform_changes(performable, 'trigger', object, User.first)
  340. new_article = object.articles.reload.last
  341. expect(new_article.internal).to be target_internal_value
  342. end
  343. end
  344. context 'when set to private' do
  345. let(:additional_options) do
  346. {
  347. notification_key => {
  348. internal: true
  349. }
  350. }
  351. end
  352. let(:target_internal_value) { true }
  353. it_behaves_like 'notification trigger'
  354. end
  355. context 'when set to internal' do
  356. let(:additional_options) do
  357. {
  358. notification_key => {
  359. internal: false
  360. }
  361. }
  362. end
  363. let(:target_internal_value) { false }
  364. it_behaves_like 'notification trigger'
  365. end
  366. context 'when no selection was made' do # ensure previously created triggers default to public
  367. let(:additional_options) do
  368. {}
  369. end
  370. let(:target_internal_value) { false }
  371. it_behaves_like 'notification trigger'
  372. end
  373. end
  374. context 'when dispatching email' do
  375. let(:notification_type) { :email }
  376. include_examples 'verify log visibility status'
  377. end
  378. shared_examples 'add a new article' do
  379. it 'adds a new article' do
  380. expect { object.perform_changes(performable, 'trigger', item, user) }
  381. .to change { object.articles.count }.by(1)
  382. end
  383. end
  384. shared_examples 'add attachment to new article' do
  385. include_examples 'add a new article'
  386. it 'adds attachment to the new article' do
  387. object.perform_changes(performable, 'trigger', item, user)
  388. article = object.articles.reload.last
  389. expect(article.type.name).to eq('email')
  390. expect(article.sender.name).to eq('System')
  391. expect(article.attachments.count).to eq(1)
  392. expect(article.attachments[0].filename).to eq('some_file.pdf')
  393. expect(article.attachments[0].preferences['Content-ID']).to eq('image/pdf@01CAB192.K8H512Y9')
  394. end
  395. end
  396. shared_examples 'does not add attachment to new article' do
  397. include_examples 'add a new article'
  398. it 'does not add attachment to the new article' do
  399. object.perform_changes(performable, 'trigger', item, user)
  400. article = object.articles.reload.last
  401. expect(article.type.name).to eq('email')
  402. expect(article.sender.name).to eq('System')
  403. expect(article.attachments.count).to eq(0)
  404. end
  405. end
  406. context 'when dispatching email with include attachment present' do
  407. let(:notification_type) { :email }
  408. let(:additional_options) do
  409. {
  410. notification_key => {
  411. include_attachments: 'true'
  412. }
  413. }
  414. end
  415. context 'when ticket has an attachment' do
  416. before do
  417. UserInfo.current_user_id = 1
  418. create(:store,
  419. object: 'Ticket::Article',
  420. o_id: ticket_article.id,
  421. data: 'dGVzdCAxMjM=',
  422. filename: 'some_file.pdf',
  423. preferences: {
  424. 'Content-Type': 'image/pdf',
  425. 'Content-ID': 'image/pdf@01CAB192.K8H512Y9',
  426. })
  427. end
  428. include_examples 'add attachment to new article'
  429. end
  430. context 'when ticket does not have an attachment' do
  431. include_examples 'does not add attachment to new article'
  432. end
  433. end
  434. context 'when dispatching email with include attachment not present' do
  435. let(:notification_type) { :email }
  436. let(:additional_options) do
  437. {
  438. notification_key => {
  439. include_attachments: 'false'
  440. }
  441. }
  442. end
  443. context 'when ticket has an attachment' do
  444. before do
  445. UserInfo.current_user_id = 1
  446. create(:store,
  447. object: 'Ticket::Article',
  448. o_id: ticket_article.id,
  449. data: 'dGVzdCAxMjM=',
  450. filename: 'some_file.pdf',
  451. preferences: {
  452. 'Content-Type': 'image/pdf',
  453. 'Content-ID': 'image/pdf@01CAB192.K8H512Y9',
  454. })
  455. end
  456. include_examples 'does not add attachment to new article'
  457. end
  458. context 'when ticket does not have an attachment' do
  459. include_examples 'does not add attachment to new article'
  460. end
  461. end
  462. context 'when dispatching SMS' do
  463. let(:notification_type) { :sms }
  464. before { create(:channel, area: 'Sms::Notification') }
  465. include_examples 'verify log visibility status'
  466. end
  467. end
  468. context 'with a "notification.webhook" trigger', performs_jobs: true do
  469. let(:webhook) { create(:webhook, endpoint: 'http://api.example.com/webhook', signature_token: '53CR3t') }
  470. let(:trigger) do
  471. create(:trigger,
  472. perform: {
  473. 'notification.webhook' => { 'webhook_id' => webhook.id }
  474. })
  475. end
  476. let(:context_data) do
  477. {
  478. type: 'info',
  479. execution: 'trigger',
  480. changes: { 'state_id' => %w[2 4] },
  481. user_id: 1,
  482. }
  483. end
  484. it 'schedules the webhooks notification job' do
  485. expect { object.perform_changes(trigger, 'trigger', context_data, 1) }.to have_enqueued_job(TriggerWebhookJob).with(
  486. trigger,
  487. object,
  488. nil,
  489. changes: { 'State' => %w[open closed] },
  490. user_id: 1,
  491. execution_type: 'trigger',
  492. event_type: 'info',
  493. )
  494. end
  495. end
  496. context 'with a "article.note" trigger' do
  497. let(:user) { create(:agent, groups: [group]) }
  498. let(:perform) do
  499. { 'article.note' => { 'subject' => 'Test subject note', 'internal' => 'true', 'body' => 'Test body note' } }
  500. end
  501. it 'adds the note' do
  502. object.perform_changes(performable, 'trigger', object, user.id)
  503. expect(object.articles.reload.last).to have_attributes(
  504. subject: 'Test subject note',
  505. body: 'Test body note',
  506. internal: true,
  507. )
  508. end
  509. end
  510. context 'with a "ticket.subscribe" trigger', current_user_id: 1 do
  511. let(:user) { create(:agent, groups: [group]) }
  512. let(:perform) do
  513. { 'ticket.subscribe' => { 'pre_condition' => 'current_user.id', 'value' => '', 'value_completion' => '' } }
  514. end
  515. it 'subscribes current user to ticket' do
  516. object.perform_changes(performable, 'trigger', object, user.id)
  517. expect(Mention.last).to have_attributes(
  518. mentionable: object,
  519. user: user,
  520. )
  521. end
  522. context 'with specific user' do
  523. let(:agent) { create(:agent, groups: [group]) }
  524. let(:perform) do
  525. { 'ticket.subscribe' => { 'pre_condition' => 'specific', 'value' => agent.id, 'value_completion' => '' } }
  526. end
  527. it 'subscribes specific user to ticket' do
  528. object.perform_changes(performable, 'trigger', object, user.id)
  529. expect(Mention.last).to have_attributes(
  530. mentionable: object,
  531. user: agent,
  532. )
  533. end
  534. end
  535. end
  536. context 'with a "ticket.unsubscribe" trigger', current_user_id: 1 do
  537. let(:user) { create(:agent, groups: [group]) }
  538. let(:other_user) { create(:agent, groups: [group]) }
  539. let!(:mention) do
  540. Mention.subscribe!(object, user)
  541. Mention.last
  542. end
  543. let!(:other_mention) do
  544. Mention.subscribe!(object, other_user)
  545. Mention.last
  546. end
  547. let(:perform) do
  548. { 'ticket.unsubscribe' => { 'pre_condition' => 'current_user.id', 'value' => '', 'value_completion' => '' } }
  549. end
  550. it 'unsubscribes current user from ticket' do
  551. object.perform_changes(performable, 'trigger', object, user.id)
  552. expect(Mention).not_to exist(mention.id)
  553. end
  554. context 'with specific user' do
  555. let(:perform) do
  556. { 'ticket.unsubscribe' => { 'pre_condition' => 'specific', 'value' => other_user.id, 'value_completion' => '' } }
  557. end
  558. it 'un subscribes specific user from ticket' do
  559. object.perform_changes(performable, 'trigger', object, other_user.id)
  560. expect(Mention).not_to exist(other_mention.id)
  561. end
  562. end
  563. context 'when unsubscribing all users' do
  564. let(:perform) do
  565. { 'ticket.unsubscribe' => { 'pre_condition' => 'not_set', 'value' => '', 'value_completion' => '' } }
  566. end
  567. it 'unsubscribes all users from ticket' do
  568. expect { object.perform_changes(performable, 'trigger', object, user.id) }
  569. .to change { object.mentions.exists? }
  570. .to false
  571. end
  572. end
  573. end
  574. describe 'Check if blocking notifications works' do
  575. context 'when mail delivery failed' do
  576. let(:ticket) { create(:ticket) }
  577. let(:customer) { create(:customer) }
  578. let(:perform) do
  579. {
  580. 'notification.email' => {
  581. body: "Hello \#{ticket.customer.firstname} \#{ticket.customer.lastname},",
  582. recipient: ["userid_#{customer.id}"],
  583. subject: "Autoclose (\#{ticket.title})",
  584. }
  585. }
  586. end
  587. context 'with a normal user' do
  588. it 'sends trigger base notification' do
  589. expect { ticket.perform_changes(performable, 'trigger', ticket, User.first) }.to change { ticket.reload.articles.count }.by(1)
  590. end
  591. end
  592. context 'with a permanent failed user' do
  593. let(:failed_date) { 1.second.ago }
  594. let(:customer) do
  595. user = create(:customer)
  596. user.preferences.merge!(mail_delivery_failed: true, mail_delivery_failed_data: failed_date)
  597. user.save!
  598. user
  599. end
  600. it 'sends no trigger base notification' do
  601. expect { ticket.perform_changes(performable, 'trigger', ticket, User.first) }.not_to change { ticket.reload.articles.count }
  602. expect(customer.reload.preferences).to include(
  603. mail_delivery_failed: true,
  604. mail_delivery_failed_data: failed_date,
  605. )
  606. end
  607. context 'with failed date 61 days ago' do
  608. let(:failed_date) { 61.days.ago }
  609. it 'sends trigger base notification' do
  610. expect { ticket.perform_changes(performable, 'trigger', ticket, User.first) }.to change { ticket.reload.articles.count }.by(1)
  611. expect(customer.reload.preferences).to include(
  612. mail_delivery_failed: false,
  613. mail_delivery_failed_data: nil,
  614. )
  615. end
  616. end
  617. context 'with failed date 70 days ago' do
  618. let(:failed_date) { 70.days.ago }
  619. it 'sends trigger base notification' do
  620. expect { ticket.perform_changes(performable, 'trigger', ticket, User.first) }.to change { ticket.reload.articles.count }.by(1)
  621. expect(customer.reload.preferences).to include(
  622. mail_delivery_failed: false,
  623. mail_delivery_failed_data: nil,
  624. )
  625. end
  626. end
  627. end
  628. end
  629. end
  630. context 'with a time-event based trigger' do
  631. let(:trigger) do
  632. condition = { 'ticket.pending_time' => { operator: 'has reached' } }
  633. perform = { 'ticket.title' => { 'value' => 'triggered' } }
  634. create(:trigger, condition:, perform:, activator: 'time', execution_condition_mode: 'always')
  635. end
  636. let(:ticket) { create(:ticket, title: 'Test Ticket', state_name: 'pending reminder', pending_time: 1.hour.ago) }
  637. before do
  638. trigger && ticket
  639. end
  640. it 'performs the trigger' do
  641. expect { Ticket.process_pending }.to change { ticket.reload.title }.to('triggered')
  642. end
  643. it 'creates related history entries' do
  644. Ticket.process_pending
  645. expect(History.last).to have_attributes(
  646. history_type_id: History::Type.find_by(name: 'time_trigger_performed').id,
  647. value_from: 'reminder_reached',
  648. sourceable_type: 'Trigger',
  649. sourceable_id: trigger.id,
  650. sourceable_name: trigger.name,
  651. )
  652. end
  653. it 'blocks the trigger from being performed again' do
  654. expect { Ticket.process_pending }.to change { ticket.reload.title }.to('triggered')
  655. Ticket.process_pending
  656. expect(History.where(history_type_id: History::Type.find_by(name: 'time_trigger_performed').id).count).to eq(1)
  657. end
  658. end
  659. end