mirror of
https://github.com/zammad/zammad
synced 2026-05-24 09:48:36 +00:00
Co-authored-by: Dominik Klein <dk@zammad.com> Co-authored-by: Mantas Masalskis <mm@zammad.com> Co-authored-by: Rene Reimann <rr@zammad.com> Co-authored-by: Florian Liebe <fl@zammad.com>
988 lines
30 KiB
Ruby
988 lines
30 KiB
Ruby
# Copyright (C) 2012-2026 Zammad Foundation, https://zammad-foundation.org/
|
|
|
|
require 'rails_helper'
|
|
require 'models/concerns/can_perform_changes_examples'
|
|
|
|
RSpec.describe 'Ticket::PerformChanges', :aggregate_failures do
|
|
subject(:object) { create(:ticket, group: group, owner: create(:agent, groups: [group])) }
|
|
|
|
let(:group) { create(:group) }
|
|
|
|
let(:performable) do
|
|
create(:trigger, perform: perform, activator: 'action', execution_condition_mode: 'always', condition: { 'ticket.state_id'=>{ 'operator' => 'is', 'value' => Ticket::State.pluck(:id) } })
|
|
end
|
|
|
|
include_examples 'CanPerformChanges', object_name: 'Ticket'
|
|
|
|
context 'when invalid data is given' do
|
|
context 'with not existing attribute' do
|
|
let(:perform) do
|
|
{
|
|
'ticket.foobar' => {
|
|
'value' => 'dummy',
|
|
}
|
|
}
|
|
end
|
|
|
|
it 'raises an error' do
|
|
expect { object.perform_changes(performable, 'trigger', object, User.first) }
|
|
.to raise_error(RuntimeError, 'The given trigger contains invalid attributes, stopping!')
|
|
end
|
|
end
|
|
|
|
context 'with invalid action in "perform" hash' do
|
|
let(:perform) do
|
|
{
|
|
'dummy' => {
|
|
'value' => 'delete',
|
|
}
|
|
}
|
|
end
|
|
|
|
it 'raises an error' do
|
|
expect { object.perform_changes(performable, 'trigger', object, User.first) }
|
|
.to raise_error(RuntimeError, 'The given trigger contains no valid actions, stopping!')
|
|
end
|
|
end
|
|
end
|
|
|
|
# Regression test for https://github.com/zammad/zammad/issues/2001
|
|
describe 'argument handling' do
|
|
let(:perform) do
|
|
{
|
|
'notification.email' => {
|
|
body: "Hello \#{ticket.customer.firstname} \#{ticket.customer.lastname},",
|
|
recipient: %w[article_last_sender ticket_owner ticket_customer ticket_agents],
|
|
subject: "Autoclose (\#{ticket.title})"
|
|
}
|
|
}
|
|
end
|
|
|
|
it 'does not mutate contents of "perform" hash' do
|
|
expect { object.perform_changes(performable, 'trigger', {}, 1) }
|
|
.not_to change { perform }
|
|
end
|
|
end
|
|
|
|
context 'with "ticket.state_id" key in "perform" hash' do
|
|
let(:perform) do
|
|
{
|
|
'ticket.state_id' => {
|
|
'value' => Ticket::State.lookup(name: 'closed').id
|
|
}
|
|
}
|
|
end
|
|
|
|
it 'changes #state to specified value' do
|
|
expect { object.perform_changes(performable, 'trigger', object, User.first) }
|
|
.to change { object.reload.state.name }.to('closed')
|
|
end
|
|
end
|
|
|
|
context 'with "ticket.state" key in "perform" hash' do
|
|
let(:perform) do
|
|
{
|
|
'ticket.state' => {
|
|
'value' => 'closed'
|
|
}
|
|
}
|
|
end
|
|
|
|
it 'changes #state to specified value' do
|
|
expect { object.perform_changes(performable, 'trigger', object, User.first) }
|
|
.to change { object.reload.state.name }.to('closed')
|
|
end
|
|
end
|
|
|
|
# Test for backwards compatibility after PR https://github.com/zammad/zammad/pull/2862
|
|
context 'with "pending_time" => { "value": DATE } in "perform" hash' do
|
|
let(:perform) do
|
|
{
|
|
'ticket.state_id' => {
|
|
'value' => Ticket::State.lookup(name: 'pending reminder').id.to_s
|
|
},
|
|
'ticket.pending_time' => {
|
|
'value' => timestamp,
|
|
},
|
|
}
|
|
end
|
|
|
|
let(:timestamp) { Time.zone.now }
|
|
|
|
it 'changes pending date to given date' do
|
|
freeze_time do
|
|
expect { object.perform_changes(performable, 'trigger', object, User.first) }
|
|
.to change(object, :pending_time)
|
|
.to timestamp.change(sec: 0)
|
|
end
|
|
end
|
|
end
|
|
|
|
# Test for PR https://github.com/zammad/zammad/pull/2862
|
|
context 'with "pending_time" => { "operator": "relative" } in "perform" hash' do
|
|
shared_examples 'verify' do
|
|
it 'verify relative pending time rule' do
|
|
freeze_time do
|
|
target_time = relative_value
|
|
.send(relative_range)
|
|
.from_now
|
|
.change(sec: 0)
|
|
|
|
expect { object.perform_changes(performable, 'trigger', object, User.first) }
|
|
.to change(object, :pending_time)
|
|
.to target_time
|
|
end
|
|
end
|
|
end
|
|
|
|
let(:perform) do
|
|
{
|
|
'ticket.state_id' => {
|
|
'value' => Ticket::State.lookup(name: 'pending reminder').id.to_s
|
|
},
|
|
'ticket.pending_time' => {
|
|
'operator' => 'relative',
|
|
'value' => relative_value,
|
|
'range' => relative_range_config
|
|
},
|
|
}
|
|
end
|
|
|
|
let(:relative_range_config) { relative_range.to_s.singularize }
|
|
|
|
context 'when value in days' do
|
|
let(:relative_value) { 2 }
|
|
let(:relative_range) { :days }
|
|
|
|
include_examples 'verify'
|
|
end
|
|
|
|
context 'when value in minutes' do
|
|
let(:relative_value) { 60 }
|
|
let(:relative_range) { :minutes }
|
|
|
|
include_examples 'verify'
|
|
end
|
|
|
|
context 'when value in weeks' do
|
|
let(:relative_value) { 2 }
|
|
let(:relative_range) { :weeks }
|
|
|
|
include_examples 'verify'
|
|
end
|
|
end
|
|
|
|
context 'with tags in "perform" hash', performs_jobs: true do
|
|
let(:user) { create(:agent, groups: [group]) }
|
|
|
|
let(:perform) do
|
|
{
|
|
'ticket.tags' => { 'operator' => tag_operator, 'value' => 'tag1, tag2' }
|
|
}
|
|
end
|
|
|
|
context 'with add' do
|
|
let(:tag_operator) { 'add' }
|
|
|
|
before do
|
|
Transaction.execute do
|
|
object
|
|
end
|
|
|
|
perform_enqueued_jobs
|
|
end
|
|
|
|
it 'adds the tags' do
|
|
expect { object.perform_changes(performable, 'trigger', object, user.id) }
|
|
.to change { object.reload.tag_list }.to(%w[tag1 tag2])
|
|
end
|
|
|
|
it 'schedules a search index update job' do
|
|
allow(SearchIndexBackend).to receive(:enabled?).and_return(true)
|
|
|
|
expect do
|
|
Transaction.execute do
|
|
object.perform_changes(performable, 'trigger')
|
|
end
|
|
end
|
|
.to have_enqueued_job(SearchIndexJob).with('Ticket', object.id)
|
|
end
|
|
end
|
|
|
|
context 'with remove' do
|
|
let(:tag_operator) { 'remove' }
|
|
|
|
before do
|
|
Transaction.execute do
|
|
object
|
|
%w[tag1 tag2].each { |tag| object.tag_add(tag, 1) }
|
|
end
|
|
|
|
perform_enqueued_jobs
|
|
end
|
|
|
|
it 'removes the tags' do
|
|
expect { object.perform_changes(performable, 'trigger', object, user.id) }
|
|
.to change { object.reload.tag_list }.to([])
|
|
end
|
|
|
|
it 'schedules a search index update job' do
|
|
allow(SearchIndexBackend).to receive(:enabled?).and_return(true)
|
|
|
|
expect do
|
|
Transaction.execute do
|
|
object.perform_changes(performable, 'trigger', object, user.id)
|
|
end
|
|
end
|
|
.to have_enqueued_job(SearchIndexJob).with('Ticket', object.id)
|
|
end
|
|
end
|
|
|
|
context 'with replace' do
|
|
let(:tag_operator) { 'replace' }
|
|
|
|
before do
|
|
Transaction.execute do
|
|
object
|
|
%w[tag0 tag1].each { |tag| object.tag_add(tag, 1) }
|
|
end
|
|
|
|
perform_enqueued_jobs
|
|
end
|
|
|
|
it 'replaces the tags' do
|
|
expect { object.perform_changes(performable, 'trigger', object, user.id) }
|
|
.to change { object.reload.tag_list }.to(%w[tag1 tag2])
|
|
end
|
|
|
|
it 'schedules a search index update job' do
|
|
allow(SearchIndexBackend).to receive(:enabled?).and_return(true)
|
|
|
|
expect do
|
|
Transaction.execute do
|
|
object.perform_changes(performable, 'trigger', object, user.id)
|
|
end
|
|
end
|
|
.to have_enqueued_job(SearchIndexJob).with('Ticket', object.id)
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'with "pre_condition" in "perform" hash' do
|
|
let(:user) { create(:agent, groups: [group]) }
|
|
|
|
let(:perform) do
|
|
{
|
|
'ticket.owner_id' => {
|
|
'pre_condition' => pre_condition,
|
|
'value' => value,
|
|
'value_completion' => '',
|
|
}
|
|
}
|
|
end
|
|
|
|
context 'with current_user.id' do
|
|
let(:pre_condition) { 'current_user.id' }
|
|
let(:value) { '' }
|
|
|
|
it 'changes to specified value' do
|
|
expect { object.perform_changes(performable, 'trigger', object, user.id) }
|
|
.to change { object.reload.owner.id }.to(user.id)
|
|
end
|
|
end
|
|
|
|
context 'with specific user' do
|
|
let(:another_user) { create(:agent, groups: [group]) }
|
|
let(:pre_condition) { 'specific' }
|
|
let(:value) { another_user.id }
|
|
|
|
it 'changes to specified value' do
|
|
expect { object.perform_changes(performable, 'trigger', object, user.id) }
|
|
.to change { object.reload.owner.id }.to(another_user.id)
|
|
end
|
|
end
|
|
|
|
context 'with current_user.id, but missing user' do
|
|
let(:pre_condition) { 'current_user.id' }
|
|
let(:value) { '' }
|
|
|
|
it 'raises an error' do
|
|
expect { object.perform_changes(performable, 'trigger', object, nil) }
|
|
.to raise_error(RuntimeError, "The required parameter 'user_id' is missing.")
|
|
end
|
|
end
|
|
|
|
context 'with not_set' do
|
|
let(:pre_condition) { 'not_set' }
|
|
let(:value) { '' }
|
|
|
|
it 'changes to user with id 1' do
|
|
expect { object.perform_changes(performable, 'trigger', object, user.id) }
|
|
.to change { object.reload.owner.id }.to(1)
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'with "ticket.action" => { "value" => "delete" } in "perform" hash' do
|
|
let(:perform) do
|
|
{
|
|
'ticket.state_id' => { 'value' => Ticket::State.lookup(name: 'closed').id.to_s },
|
|
'ticket.action' => { 'value' => 'delete' },
|
|
}
|
|
end
|
|
|
|
it 'performs a ticket deletion on a ticket' do
|
|
expect { object.perform_changes(performable, 'trigger', object, User.first) }
|
|
.to change(object, :destroyed?).to(true)
|
|
end
|
|
end
|
|
|
|
context 'with a "notification.email" trigger' do
|
|
# Regression test for https://github.com/zammad/zammad/issues/1543
|
|
#
|
|
# If a new article fires an email notification trigger,
|
|
# and then another article is added to the same ticket
|
|
# before that trigger is performed,
|
|
# the email template's 'article' var should refer to the originating article,
|
|
# not the newest one.
|
|
#
|
|
# (This occurs whenever one action fires multiple email notification triggers.)
|
|
context 'when two articles are created before the trigger fires once (race condition)' do
|
|
let!(:article) { create(:ticket_article, ticket: object) }
|
|
let!(:new_article) { create(:ticket_article, ticket: object) }
|
|
|
|
let(:trigger) do
|
|
build(:trigger,
|
|
perform: {
|
|
'notification.email' => {
|
|
body: 'Sample notification',
|
|
recipient: 'ticket_customer',
|
|
subject: 'Sample subject'
|
|
}
|
|
})
|
|
end
|
|
|
|
let(:objects) do
|
|
last_article = nil
|
|
last_internal_article = nil
|
|
last_external_article = nil
|
|
all_articles = object.articles
|
|
|
|
if article.nil?
|
|
last_article = all_articles.last
|
|
last_internal_article = all_articles.reverse.find(&:internal?)
|
|
last_external_article = all_articles.reverse.find { |a| !a.internal? }
|
|
else
|
|
last_article = article
|
|
last_internal_article = article.internal? ? article : all_articles.reverse.find(&:internal?)
|
|
last_external_article = article.internal? ? all_articles.reverse.find { |a| !a.internal? } : article
|
|
end
|
|
|
|
{
|
|
ticket: object,
|
|
article: last_article,
|
|
last_article: last_article,
|
|
last_internal_article: last_internal_article,
|
|
last_external_article: last_external_article,
|
|
created_article: article,
|
|
created_internal_article: article&.internal? ? article : nil,
|
|
created_external_article: article&.internal? ? nil : article,
|
|
}
|
|
end
|
|
|
|
# required by Ticket#perform_changes for email notifications
|
|
before do
|
|
allow(NotificationFactory::Mailer).to receive(:template).and_call_original
|
|
|
|
article.ticket.group.update(email_address: create(:email_address))
|
|
end
|
|
|
|
it 'passes the first article to NotificationFactory::Mailer' do
|
|
object.perform_changes(trigger, 'trigger', { article_id: article.id }, 1)
|
|
|
|
expect(NotificationFactory::Mailer)
|
|
.to have_received(:template)
|
|
.with(hash_including(objects: hash_including(objects)))
|
|
.at_least(:once)
|
|
|
|
expect(NotificationFactory::Mailer)
|
|
.not_to have_received(:template)
|
|
.with(hash_including(objects: { ticket: object, article: new_article }))
|
|
end
|
|
end
|
|
|
|
context 'when dispatching email through an inactive channel' do
|
|
let!(:article) { create(:ticket_article, ticket: object) }
|
|
let(:trigger) do
|
|
build(:trigger,
|
|
perform: {
|
|
'notification.email' => {
|
|
body: 'Sample notification',
|
|
recipient: 'ticket_customer',
|
|
subject: 'Sample subject'
|
|
}
|
|
})
|
|
end
|
|
|
|
# required by Ticket#perform_changes for email notifications
|
|
before do
|
|
allow(NotificationFactory::Mailer).to receive(:template).and_call_original
|
|
allow(Rails.logger).to receive(:info)
|
|
|
|
article.ticket.group.update(email_address: create(:email_address, channel: create(:channel, active: false)))
|
|
end
|
|
|
|
it 'does not pass the article to NotificationFactory::Mailer' do
|
|
object.perform_changes(trigger, 'trigger', { article_id: article.id }, 1)
|
|
|
|
expect(Rails.logger).to have_received(:info).with(match(%r{because the channel .* is not active}))
|
|
|
|
# no specific email content awaiting needed, since we do not expect to receive any mail (what ever it is) at the point
|
|
expect(NotificationFactory::Mailer).not_to have_received(:template)
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'with a notification trigger' do
|
|
# https://github.com/zammad/zammad/issues/2782
|
|
#
|
|
# Notification triggers should log notification as private or public
|
|
# according to given configuration
|
|
let(:user) { create(:admin, mobile: '+37061010000') }
|
|
let(:perform) do
|
|
{
|
|
notification_key => {
|
|
body: 'Old programmers never die. They just branch to a new address.',
|
|
recipient: 'ticket_agents',
|
|
subject: 'Old programmers never die. They just branch to a new address.'
|
|
}
|
|
}.deep_merge(additional_options).deep_stringify_keys
|
|
end
|
|
let(:notification_key) { "notification.#{notification_type}" }
|
|
let!(:ticket_article) { create(:ticket_article, ticket: object) }
|
|
let(:item) do
|
|
{
|
|
object: 'Ticket',
|
|
object_id: object.id,
|
|
user_id: user.id,
|
|
type: 'update',
|
|
article_id: ticket_article.id
|
|
}
|
|
end
|
|
|
|
before { object.group.users << user }
|
|
|
|
shared_examples 'verify log visibility status' do
|
|
shared_examples 'notification trigger' do
|
|
it 'adds Ticket::Article' do
|
|
expect { object.perform_changes(performable, 'trigger', object, user) }
|
|
.to change { object.articles.count }.by(1)
|
|
end
|
|
|
|
it 'new Ticket::Article visibility reflects setting' do
|
|
object.perform_changes(performable, 'trigger', object, User.first)
|
|
new_article = object.articles.reload.last
|
|
expect(new_article.internal).to be target_internal_value
|
|
end
|
|
end
|
|
|
|
context 'when set to private' do
|
|
let(:additional_options) do
|
|
{
|
|
notification_key => {
|
|
internal: true
|
|
}
|
|
}
|
|
end
|
|
|
|
let(:target_internal_value) { true }
|
|
|
|
it_behaves_like 'notification trigger'
|
|
end
|
|
|
|
context 'when set to internal' do
|
|
let(:additional_options) do
|
|
{
|
|
notification_key => {
|
|
internal: false
|
|
}
|
|
}
|
|
end
|
|
|
|
let(:target_internal_value) { false }
|
|
|
|
it_behaves_like 'notification trigger'
|
|
end
|
|
|
|
context 'when no selection was made' do # ensure previously created triggers default to public
|
|
let(:additional_options) do
|
|
{}
|
|
end
|
|
|
|
let(:target_internal_value) { false }
|
|
|
|
it_behaves_like 'notification trigger'
|
|
end
|
|
end
|
|
|
|
context 'when dispatching email' do
|
|
let(:notification_type) { :email }
|
|
|
|
include_examples 'verify log visibility status'
|
|
end
|
|
|
|
shared_examples 'add a new article' do
|
|
it 'adds a new article' do
|
|
expect { object.perform_changes(performable, 'trigger', item, user) }
|
|
.to change { object.articles.count }.by(1)
|
|
end
|
|
end
|
|
|
|
shared_examples 'add attachment to new article' do
|
|
include_examples 'add a new article'
|
|
|
|
it 'adds attachment to the new article' do
|
|
object.perform_changes(performable, 'trigger', item, user)
|
|
article = object.articles.reload.last
|
|
|
|
expect(article.type.name).to eq('email')
|
|
expect(article.sender.name).to eq('System')
|
|
expect(article.attachments.count).to eq(1)
|
|
expect(article.attachments[0].filename).to eq('some_file.pdf')
|
|
expect(article.attachments[0].preferences['Content-ID']).to eq('image/pdf@01CAB192.K8H512Y9')
|
|
end
|
|
end
|
|
|
|
shared_examples 'does not add attachment to new article' do
|
|
include_examples 'add a new article'
|
|
|
|
it 'does not add attachment to the new article' do
|
|
object.perform_changes(performable, 'trigger', item, user)
|
|
article = object.articles.reload.last
|
|
|
|
expect(article.type.name).to eq('email')
|
|
expect(article.sender.name).to eq('System')
|
|
expect(article.attachments.count).to eq(0)
|
|
end
|
|
end
|
|
|
|
context 'when dispatching email with include attachment present' do
|
|
let(:notification_type) { :email }
|
|
let(:additional_options) do
|
|
{
|
|
notification_key => {
|
|
include_attachments: 'true'
|
|
}
|
|
}
|
|
end
|
|
|
|
context 'when ticket has an attachment' do
|
|
|
|
before do
|
|
UserInfo.current_user_id = 1
|
|
|
|
create(:store,
|
|
object: 'Ticket::Article',
|
|
o_id: ticket_article.id,
|
|
data: 'dGVzdCAxMjM=',
|
|
filename: 'some_file.pdf',
|
|
preferences: {
|
|
'Content-Type': 'image/pdf',
|
|
'Content-ID': 'image/pdf@01CAB192.K8H512Y9',
|
|
})
|
|
end
|
|
|
|
include_examples 'add attachment to new article'
|
|
end
|
|
|
|
context 'when ticket does not have an attachment' do
|
|
|
|
include_examples 'does not add attachment to new article'
|
|
end
|
|
end
|
|
|
|
context 'when dispatching email with include attachment not present' do
|
|
let(:notification_type) { :email }
|
|
let(:additional_options) do
|
|
{
|
|
notification_key => {
|
|
include_attachments: 'false'
|
|
}
|
|
}
|
|
end
|
|
|
|
context 'when ticket has an attachment' do
|
|
|
|
before do
|
|
UserInfo.current_user_id = 1
|
|
|
|
create(:store,
|
|
object: 'Ticket::Article',
|
|
o_id: ticket_article.id,
|
|
data: 'dGVzdCAxMjM=',
|
|
filename: 'some_file.pdf',
|
|
preferences: {
|
|
'Content-Type': 'image/pdf',
|
|
'Content-ID': 'image/pdf@01CAB192.K8H512Y9',
|
|
})
|
|
end
|
|
|
|
include_examples 'does not add attachment to new article'
|
|
end
|
|
|
|
context 'when ticket does not have an attachment' do
|
|
|
|
include_examples 'does not add attachment to new article'
|
|
end
|
|
end
|
|
|
|
context 'when dispatching SMS' do
|
|
let(:notification_type) { :sms }
|
|
|
|
before { create(:channel, area: 'Sms::Notification') }
|
|
|
|
include_examples 'verify log visibility status'
|
|
end
|
|
end
|
|
|
|
context 'with a "notification.webhook" trigger', performs_jobs: true do
|
|
let(:webhook) { create(:webhook, endpoint: 'http://api.example.com/webhook', signature_token: '53CR3t') }
|
|
let(:trigger) do
|
|
create(:trigger,
|
|
perform: {
|
|
'notification.webhook' => { 'webhook_id' => webhook.id }
|
|
})
|
|
end
|
|
|
|
let(:context_data) do
|
|
{
|
|
type: 'info',
|
|
execution: 'trigger',
|
|
changes: { 'state_id' => %w[2 4] },
|
|
user_id: 1,
|
|
}
|
|
end
|
|
|
|
before do
|
|
allow(IPSocket)
|
|
.to receive(:getaddress)
|
|
.with('api.example.com')
|
|
.and_return('8.8.8.8')
|
|
end
|
|
|
|
it 'schedules the webhooks notification job' do
|
|
expect { object.perform_changes(trigger, 'trigger', context_data, 1) }.to have_enqueued_job(TriggerWebhookJob).with(
|
|
trigger,
|
|
object,
|
|
nil,
|
|
changes: { 'State' => %w[open closed] },
|
|
user_id: 1,
|
|
execution_type: 'trigger',
|
|
event_type: 'info',
|
|
)
|
|
end
|
|
end
|
|
|
|
context 'with a "ai.ai_agent" trigger', performs_jobs: true do
|
|
let(:ai_agent) { create(:ai_agent) }
|
|
let(:trigger) do
|
|
create(:trigger,
|
|
perform: {
|
|
'ai.ai_agent' => { 'ai_agent_id' => ai_agent.id }
|
|
})
|
|
end
|
|
|
|
let(:context_data) do
|
|
{
|
|
type: 'info',
|
|
execution: 'trigger',
|
|
changes: { 'state_id' => %w[2 4] },
|
|
user_id: 1,
|
|
}
|
|
end
|
|
|
|
it 'schedules the webhooks notification job' do
|
|
expect { object.perform_changes(trigger, 'trigger', context_data, 1) }
|
|
.to have_enqueued_job(TriggerAIAgentJob).with(
|
|
ai_agent,
|
|
object,
|
|
nil,
|
|
changes: { 'State' => %w[open closed] },
|
|
user_id: 1,
|
|
execution_type: 'trigger',
|
|
event_type: 'info',
|
|
)
|
|
end
|
|
end
|
|
|
|
context 'with a "article.note" trigger' do
|
|
let(:user) { create(:agent, groups: [group]) }
|
|
|
|
let(:perform) do
|
|
{ 'article.note' => { 'subject' => 'Test subject note', 'internal' => 'true', 'body' => 'Test body note' } }
|
|
end
|
|
|
|
it 'adds the note' do
|
|
object.perform_changes(performable, 'trigger', object, user.id)
|
|
|
|
expect(object.articles.reload.last).to have_attributes(
|
|
subject: 'Test subject note',
|
|
body: 'Test body note',
|
|
internal: true,
|
|
)
|
|
end
|
|
|
|
context 'when note is added with current user variable' do
|
|
let(:perform) do
|
|
{ 'article.note' => { 'subject' => 'Test subject note', 'internal' => 'true', 'body' => "Test body note from \#{user.firstname}" } }
|
|
end
|
|
|
|
it 'adds the note with the current user' do
|
|
object.perform_changes(performable, 'trigger', object, user.id)
|
|
|
|
expect(object.articles.reload.last).to have_attributes(
|
|
subject: 'Test subject note',
|
|
body: "Test body note from #{user.firstname}",
|
|
internal: true,
|
|
)
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'with a "ticket.subscribe" trigger for non-agent user', current_user_id: 1 do
|
|
let(:user) { create(:customer) }
|
|
|
|
let(:perform) do
|
|
{ 'ticket.subscribe' => { 'pre_condition' => 'current_user.id', 'value' => '', 'value_completion' => '' } }
|
|
end
|
|
|
|
it 'does not subscribe customer to ticket' do
|
|
object.perform_changes(performable, 'trigger', object, user.id)
|
|
|
|
expect(Mention.exists?(mentionable: object, user: user)).to be false
|
|
end
|
|
|
|
context 'with specific user' do
|
|
let(:customer) { create(:customer) }
|
|
let(:perform) do
|
|
{ 'ticket.subscribe' => { 'pre_condition' => 'specific', 'value' => customer.id, 'value_completion' => '' } }
|
|
end
|
|
|
|
it 'does not subscribe specific customer to ticket' do
|
|
object.perform_changes(performable, 'trigger', object, user.id)
|
|
|
|
expect(Mention.exists?(mentionable: object, user: customer)).to be false
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'with a "ticket.subscribe" trigger', current_user_id: 1 do
|
|
let(:user) { create(:agent, groups: [group]) }
|
|
|
|
let(:perform) do
|
|
{ 'ticket.subscribe' => { 'pre_condition' => 'current_user.id', 'value' => '', 'value_completion' => '' } }
|
|
end
|
|
|
|
it 'subscribes current user to ticket' do
|
|
object.perform_changes(performable, 'trigger', object, user.id)
|
|
|
|
expect(Mention.last).to have_attributes(
|
|
mentionable: object,
|
|
user: user,
|
|
)
|
|
|
|
expect(History.last).to have_attributes(
|
|
o_id: Mention.last.id,
|
|
related_o_id: object.id,
|
|
sourceable_type: 'Trigger',
|
|
sourceable_id: performable.id,
|
|
sourceable_name: performable.name,
|
|
)
|
|
end
|
|
|
|
context 'with specific user' do
|
|
let(:agent) { create(:agent, groups: [group]) }
|
|
let(:perform) do
|
|
{ 'ticket.subscribe' => { 'pre_condition' => 'specific', 'value' => agent.id, 'value_completion' => '' } }
|
|
end
|
|
|
|
it 'subscribes specific user to ticket' do
|
|
object.perform_changes(performable, 'trigger', object, user.id)
|
|
|
|
expect(Mention.last).to have_attributes(
|
|
mentionable: object,
|
|
user: agent,
|
|
)
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'with a "ticket.unsubscribe" trigger', current_user_id: 1 do
|
|
let(:user) { create(:agent, groups: [group]) }
|
|
let(:other_user) { create(:agent, groups: [group]) }
|
|
let!(:mention) do
|
|
Mention.subscribe!(object, user)
|
|
Mention.last
|
|
end
|
|
let!(:other_mention) do
|
|
Mention.subscribe!(object, other_user)
|
|
Mention.last
|
|
end
|
|
|
|
let(:perform) do
|
|
{ 'ticket.unsubscribe' => { 'pre_condition' => 'current_user.id', 'value' => '', 'value_completion' => '' } }
|
|
end
|
|
|
|
it 'unsubscribes current user from ticket' do
|
|
object.perform_changes(performable, 'trigger', object, user.id)
|
|
|
|
expect(Mention).not_to exist(mention.id)
|
|
expect(History.last).to have_attributes(
|
|
o_id: mention.id,
|
|
related_o_id: object.id,
|
|
sourceable_type: 'Trigger',
|
|
sourceable_id: performable.id,
|
|
sourceable_name: performable.name,
|
|
)
|
|
end
|
|
|
|
context 'with specific user' do
|
|
let(:perform) do
|
|
{ 'ticket.unsubscribe' => { 'pre_condition' => 'specific', 'value' => other_user.id, 'value_completion' => '' } }
|
|
end
|
|
|
|
it 'un subscribes specific user from ticket' do
|
|
object.perform_changes(performable, 'trigger', object, other_user.id)
|
|
|
|
expect(Mention).not_to exist(other_mention.id)
|
|
end
|
|
end
|
|
|
|
context 'when unsubscribing all users' do
|
|
let(:perform) do
|
|
{ 'ticket.unsubscribe' => { 'pre_condition' => 'not_set', 'value' => '', 'value_completion' => '' } }
|
|
end
|
|
|
|
it 'unsubscribes all users from ticket' do
|
|
expect { object.perform_changes(performable, 'trigger', object, user.id) }
|
|
.to change { object.mentions.exists? }
|
|
.to false
|
|
|
|
expect(History.last).to have_attributes(
|
|
o_id: other_mention.id,
|
|
related_o_id: object.id,
|
|
sourceable_type: 'Trigger',
|
|
sourceable_id: performable.id,
|
|
sourceable_name: performable.name,
|
|
)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'Check if blocking notifications works' do
|
|
context 'when mail delivery failed' do
|
|
let(:ticket) { create(:ticket) }
|
|
let(:customer) { create(:customer) }
|
|
|
|
let(:perform) do
|
|
{
|
|
'notification.email' => {
|
|
body: "Hello \#{ticket.customer.firstname} \#{ticket.customer.lastname},",
|
|
recipient: ["userid_#{customer.id}"],
|
|
subject: "Autoclose (\#{ticket.title})",
|
|
}
|
|
}
|
|
end
|
|
|
|
context 'with a normal user' do
|
|
it 'sends trigger base notification' do
|
|
expect { ticket.perform_changes(performable, 'trigger', ticket, User.first) }.to change { ticket.reload.articles.count }.by(1)
|
|
end
|
|
end
|
|
|
|
context 'with a permanent failed user' do
|
|
let(:failed_date) { 1.second.ago }
|
|
|
|
let(:customer) do
|
|
user = create(:customer)
|
|
user.preferences.merge!(mail_delivery_failed: true, mail_delivery_failed_data: failed_date)
|
|
user.save!
|
|
user
|
|
end
|
|
|
|
it 'sends no trigger base notification' do
|
|
expect { ticket.perform_changes(performable, 'trigger', ticket, User.first) }.not_to change { ticket.reload.articles.count }
|
|
|
|
expect(customer.reload.preferences).to include(
|
|
mail_delivery_failed: true,
|
|
mail_delivery_failed_data: failed_date,
|
|
)
|
|
end
|
|
|
|
context 'with failed date 61 days ago' do
|
|
let(:failed_date) { 61.days.ago }
|
|
|
|
it 'sends trigger base notification' do
|
|
expect { ticket.perform_changes(performable, 'trigger', ticket, User.first) }.to change { ticket.reload.articles.count }.by(1)
|
|
|
|
expect(customer.reload.preferences).to include(
|
|
mail_delivery_failed: false,
|
|
mail_delivery_failed_data: nil,
|
|
)
|
|
end
|
|
end
|
|
|
|
context 'with failed date 70 days ago' do
|
|
let(:failed_date) { 70.days.ago }
|
|
|
|
it 'sends trigger base notification' do
|
|
expect { ticket.perform_changes(performable, 'trigger', ticket, User.first) }.to change { ticket.reload.articles.count }.by(1)
|
|
|
|
expect(customer.reload.preferences).to include(
|
|
mail_delivery_failed: false,
|
|
mail_delivery_failed_data: nil,
|
|
)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'with a time-event based trigger' do
|
|
let(:trigger) do
|
|
condition = { 'ticket.pending_time' => { operator: 'has reached' } }
|
|
perform = { 'ticket.title' => { 'value' => 'triggered' } }
|
|
|
|
create(:trigger, condition:, perform:, activator: 'time', execution_condition_mode: 'always')
|
|
end
|
|
|
|
let(:ticket) { create(:ticket, title: 'Test Ticket', state_name: 'pending reminder', pending_time: 1.hour.ago) }
|
|
|
|
before do
|
|
trigger && ticket
|
|
end
|
|
|
|
it 'performs the trigger' do
|
|
expect { Ticket.process_pending }.to change { ticket.reload.title }.to('triggered')
|
|
end
|
|
|
|
it 'creates related history entries' do
|
|
Ticket.process_pending
|
|
|
|
expect(History.last).to have_attributes(
|
|
history_type_id: History::Type.find_by(name: 'time_trigger_performed').id,
|
|
value_from: 'reminder_reached',
|
|
sourceable_type: 'Trigger',
|
|
sourceable_id: trigger.id,
|
|
sourceable_name: trigger.name,
|
|
)
|
|
end
|
|
|
|
it 'blocks the trigger from being performed again' do
|
|
expect { Ticket.process_pending }.to change { ticket.reload.title }.to('triggered')
|
|
|
|
Ticket.process_pending
|
|
expect(History.where(history_type_id: History::Type.find_by(name: 'time_trigger_performed').id).count).to eq(1)
|
|
end
|
|
end
|
|
end
|