2017-05-02 11:37:20 +00:00
|
|
|
require 'rails_helper'
|
2019-01-22 16:35:01 +00:00
|
|
|
require 'models/application_model_examples'
|
2018-07-05 15:43:52 +00:00
|
|
|
require 'models/concerns/can_lookup_examples'
|
2017-05-02 11:37:20 +00:00
|
|
|
|
2019-01-22 16:35:01 +00:00
|
|
|
RSpec.describe Ticket, type: :model do
|
|
|
|
include_examples 'ApplicationModel'
|
2018-07-05 13:24:26 +00:00
|
|
|
include_examples 'CanLookup'
|
2017-05-02 11:37:20 +00:00
|
|
|
|
2017-06-28 08:24:36 +00:00
|
|
|
describe '#merge_to' do
|
2017-05-02 11:37:20 +00:00
|
|
|
|
|
|
|
it 'reassigns all links to the target ticket after merge' do
|
|
|
|
source_ticket = create(:ticket)
|
|
|
|
target_ticket = create(:ticket)
|
|
|
|
|
|
|
|
important_ticket1 = create(:ticket)
|
|
|
|
important_ticket2 = create(:ticket)
|
|
|
|
important_ticket3 = create(:ticket)
|
|
|
|
|
|
|
|
create(:link, link_object_source_value: source_ticket.id, link_object_target_value: important_ticket1.id)
|
|
|
|
create(:link, link_object_source_value: source_ticket.id, link_object_target_value: important_ticket2.id)
|
|
|
|
create(:link, link_object_source_value: source_ticket.id, link_object_target_value: important_ticket3.id)
|
|
|
|
|
|
|
|
source_ticket.merge_to(
|
|
|
|
ticket_id: target_ticket.id,
|
|
|
|
user_id: 1,
|
|
|
|
)
|
|
|
|
|
|
|
|
links = Link.list(
|
2018-12-19 17:31:51 +00:00
|
|
|
link_object: 'Ticket',
|
2017-05-02 11:37:20 +00:00
|
|
|
link_object_value: target_ticket.id,
|
|
|
|
)
|
|
|
|
|
|
|
|
expected_ticket_ids = [source_ticket.id, important_ticket1.id, important_ticket2.id, important_ticket3.id ]
|
|
|
|
check_ticket_ids = links.collect { |link| link['link_object_value'] }
|
|
|
|
|
|
|
|
expect(check_ticket_ids).to match_array(expected_ticket_ids)
|
|
|
|
end
|
2017-05-05 09:16:47 +00:00
|
|
|
|
2017-07-19 10:03:17 +00:00
|
|
|
it 'prevents cross merging tickets' do
|
|
|
|
source_ticket = create(:ticket)
|
|
|
|
target_ticket = create(:ticket)
|
|
|
|
|
|
|
|
result = source_ticket.merge_to(
|
|
|
|
ticket_id: target_ticket.id,
|
|
|
|
user_id: 1,
|
|
|
|
)
|
|
|
|
expect(result).to be(true)
|
|
|
|
|
2017-10-01 12:25:52 +00:00
|
|
|
expect do
|
2017-07-19 10:03:17 +00:00
|
|
|
result = target_ticket.merge_to(
|
|
|
|
ticket_id: source_ticket.id,
|
|
|
|
user_id: 1,
|
|
|
|
)
|
2017-10-01 12:25:52 +00:00
|
|
|
end.to raise_error('ticket already merged, no merge into merged ticket possible')
|
2017-07-25 11:53:01 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
it 'prevents merging ticket in it self' do
|
|
|
|
source_ticket = create(:ticket)
|
|
|
|
|
2017-10-01 12:25:52 +00:00
|
|
|
expect do
|
2017-07-25 11:53:01 +00:00
|
|
|
result = source_ticket.merge_to(
|
|
|
|
ticket_id: source_ticket.id,
|
|
|
|
user_id: 1,
|
|
|
|
)
|
2017-10-01 12:25:52 +00:00
|
|
|
end.to raise_error('Can\'t merge ticket with it self!')
|
2017-07-19 10:03:17 +00:00
|
|
|
end
|
|
|
|
|
2017-05-05 09:16:47 +00:00
|
|
|
end
|
|
|
|
|
2019-01-04 15:29:56 +00:00
|
|
|
describe '.create' do
|
|
|
|
it 'handles NULL byte in title' do
|
|
|
|
expect(create(:ticket, title: "some title \u0000 123"))
|
|
|
|
.to be_persisted
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-06-28 08:24:36 +00:00
|
|
|
describe '#destroy' do
|
2017-05-05 09:16:47 +00:00
|
|
|
|
|
|
|
it 'deletes all related objects before destroy' do
|
|
|
|
ApplicationHandleInfo.current = 'application_server'
|
|
|
|
|
|
|
|
source_ticket = create(:ticket)
|
|
|
|
|
|
|
|
# create some links
|
|
|
|
important_ticket1 = create(:ticket)
|
|
|
|
important_ticket2 = create(:ticket)
|
|
|
|
important_ticket3 = create(:ticket)
|
|
|
|
|
|
|
|
# create some articles
|
|
|
|
create(:ticket_article, ticket_id: source_ticket.id)
|
|
|
|
create(:ticket_article, ticket_id: source_ticket.id)
|
|
|
|
create(:ticket_article, ticket_id: source_ticket.id)
|
|
|
|
|
|
|
|
create(:link, link_object_source_value: source_ticket.id, link_object_target_value: important_ticket1.id)
|
|
|
|
create(:link, link_object_source_value: important_ticket2.id, link_object_target_value: source_ticket.id)
|
|
|
|
create(:link, link_object_source_value: source_ticket.id, link_object_target_value: important_ticket3.id)
|
|
|
|
|
|
|
|
create(:online_notification, o_id: source_ticket.id)
|
|
|
|
create(:tag, o_id: source_ticket.id)
|
|
|
|
|
|
|
|
Observer::Transaction.commit
|
|
|
|
Scheduler.worker(true)
|
|
|
|
|
|
|
|
# get before destroy
|
|
|
|
activities = ActivityStream.where(
|
|
|
|
activity_stream_object_id: ObjectLookup.by_name('Ticket'),
|
2018-12-19 17:31:51 +00:00
|
|
|
o_id: source_ticket.id,
|
2017-05-05 09:16:47 +00:00
|
|
|
)
|
|
|
|
links = Link.list(
|
2018-12-19 17:31:51 +00:00
|
|
|
link_object: 'Ticket',
|
2017-05-05 09:16:47 +00:00
|
|
|
link_object_value: source_ticket.id
|
|
|
|
)
|
|
|
|
articles = Ticket::Article.where(ticket_id: source_ticket.id)
|
|
|
|
history = History.list('Ticket', source_ticket.id, nil, true)
|
|
|
|
karma_log = Karma::ActivityLog.where(
|
|
|
|
object_lookup_id: ObjectLookup.by_name('Ticket'),
|
2018-12-19 17:31:51 +00:00
|
|
|
o_id: source_ticket.id,
|
2017-05-05 09:16:47 +00:00
|
|
|
)
|
|
|
|
online_notifications = OnlineNotification.where(
|
|
|
|
object_lookup_id: ObjectLookup.by_name('Ticket'),
|
2018-12-19 17:31:51 +00:00
|
|
|
o_id: source_ticket.id,
|
2017-05-05 09:16:47 +00:00
|
|
|
)
|
|
|
|
recent_views = OnlineNotification.where(
|
|
|
|
object_lookup_id: ObjectLookup.by_name('Ticket'),
|
2018-12-19 17:31:51 +00:00
|
|
|
o_id: source_ticket.id,
|
2017-05-05 09:16:47 +00:00
|
|
|
)
|
|
|
|
tags = Tag.tag_list(
|
|
|
|
object: 'Ticket',
|
2018-12-19 17:31:51 +00:00
|
|
|
o_id: source_ticket.id,
|
2017-05-05 09:16:47 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
# check before destroy
|
|
|
|
expect(activities.count).to be >= 0
|
|
|
|
expect(links.count).to be >= 0
|
|
|
|
expect(articles.count).to be >= 0
|
|
|
|
expect(history[:list].count).to be >= 0
|
|
|
|
expect(karma_log.count).to be >= 0
|
|
|
|
expect(online_notifications.count).to be >= 0
|
|
|
|
expect(recent_views.count).to be >= 0
|
|
|
|
expect(tags.count).to be >= 0
|
|
|
|
|
|
|
|
# destroy ticket
|
|
|
|
source_ticket.destroy
|
|
|
|
|
|
|
|
# get after destroy
|
|
|
|
activities = ActivityStream.where(
|
|
|
|
activity_stream_object_id: ObjectLookup.by_name('Ticket'),
|
2018-12-19 17:31:51 +00:00
|
|
|
o_id: source_ticket.id,
|
2017-05-05 09:16:47 +00:00
|
|
|
)
|
|
|
|
links = Link.list(
|
2018-12-19 17:31:51 +00:00
|
|
|
link_object: 'Ticket',
|
2017-05-05 09:16:47 +00:00
|
|
|
link_object_value: source_ticket.id
|
|
|
|
)
|
|
|
|
articles = Ticket::Article.where(ticket_id: source_ticket.id)
|
|
|
|
history = History.list('Ticket', source_ticket.id, nil, true)
|
|
|
|
karma_log = Karma::ActivityLog.where(
|
|
|
|
object_lookup_id: ObjectLookup.by_name('Ticket'),
|
2018-12-19 17:31:51 +00:00
|
|
|
o_id: source_ticket.id,
|
2017-05-05 09:16:47 +00:00
|
|
|
)
|
|
|
|
online_notifications = OnlineNotification.where(
|
|
|
|
object_lookup_id: ObjectLookup.by_name('Ticket'),
|
2018-12-19 17:31:51 +00:00
|
|
|
o_id: source_ticket.id,
|
2017-05-05 09:16:47 +00:00
|
|
|
)
|
|
|
|
recent_views = OnlineNotification.where(
|
|
|
|
object_lookup_id: ObjectLookup.by_name('Ticket'),
|
2018-12-19 17:31:51 +00:00
|
|
|
o_id: source_ticket.id,
|
2017-05-05 09:16:47 +00:00
|
|
|
)
|
|
|
|
tags = Tag.tag_list(
|
|
|
|
object: 'Ticket',
|
2018-12-19 17:31:51 +00:00
|
|
|
o_id: source_ticket.id,
|
2017-05-05 09:16:47 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
# check after destroy
|
|
|
|
expect(activities.count).to be == 0
|
|
|
|
expect(links.count).to be == 0
|
|
|
|
expect(articles.count).to be == 0
|
|
|
|
expect(history[:list].count).to be == 0
|
|
|
|
expect(karma_log.count).to be == 0
|
|
|
|
expect(online_notifications.count).to be == 0
|
|
|
|
expect(recent_views.count).to be == 0
|
|
|
|
expect(tags.count).to be == 0
|
|
|
|
|
|
|
|
end
|
|
|
|
|
2017-05-02 11:37:20 +00:00
|
|
|
end
|
2017-05-05 09:16:47 +00:00
|
|
|
|
2017-06-28 08:24:36 +00:00
|
|
|
describe '#perform_changes' do
|
2017-05-05 09:16:47 +00:00
|
|
|
|
2018-05-15 10:18:14 +00:00
|
|
|
it 'performs a ticket state change on a ticket' do
|
2017-05-05 09:16:47 +00:00
|
|
|
source_ticket = create(:ticket)
|
|
|
|
|
|
|
|
changes = {
|
|
|
|
'ticket.state_id' => { 'value' => Ticket::State.lookup(name: 'closed').id.to_s },
|
|
|
|
}
|
|
|
|
|
|
|
|
source_ticket.perform_changes(changes, 'trigger', source_ticket, User.find(1))
|
|
|
|
source_ticket.reload
|
|
|
|
|
|
|
|
expect(source_ticket.state.name).to eq('closed')
|
|
|
|
end
|
|
|
|
|
2018-05-15 10:18:14 +00:00
|
|
|
it 'performs a ticket deletion on a ticket' do
|
2017-05-05 09:16:47 +00:00
|
|
|
source_ticket = create(:ticket)
|
|
|
|
|
|
|
|
changes = {
|
|
|
|
'ticket.state_id' => { 'value' => Ticket::State.lookup(name: 'closed').id.to_s },
|
2018-12-19 17:31:51 +00:00
|
|
|
'ticket.action' => { 'value' => 'delete' },
|
2017-05-05 09:16:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
source_ticket.perform_changes(changes, 'trigger', source_ticket, User.find(1))
|
|
|
|
ticket_with_source_ids = Ticket.where(id: source_ticket.id)
|
|
|
|
expect(ticket_with_source_ids).to match_array([])
|
|
|
|
end
|
|
|
|
|
2018-05-15 10:18:14 +00:00
|
|
|
# Regression test for https://github.com/zammad/zammad/issues/2001
|
|
|
|
it 'does not modify its arguments' do
|
|
|
|
trigger = Trigger.new(
|
|
|
|
perform: {
|
|
|
|
'notification.email' => {
|
2018-12-19 17:31:51 +00:00
|
|
|
body: "Hello \#{ticket.customer.firstname} \#{ticket.customer.lastname},",
|
2018-05-15 10:18:14 +00:00
|
|
|
recipient: %w[article_last_sender ticket_owner ticket_customer ticket_agents],
|
2018-12-19 17:31:51 +00:00
|
|
|
subject: "Autoclose (\#{ticket.title})"
|
2018-05-15 10:18:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
expect { Ticket.first.perform_changes(trigger.perform, 'trigger', {}, 1) }
|
|
|
|
.to not_change { trigger.perform['notification.email'][:body] }
|
|
|
|
.and not_change { trigger.perform['notification.email'][:subject] }
|
|
|
|
end
|
|
|
|
|
2018-05-23 10:25:11 +00:00
|
|
|
# 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.)
|
|
|
|
it 'passes the correct article to NotificationFactory::Mailer' do
|
|
|
|
# required by Ticket#perform_changes for email notifications
|
|
|
|
Group.first.update(email_address: create(:email_address))
|
|
|
|
|
|
|
|
ticket = Ticket.first
|
|
|
|
orig_article = Ticket::Article.where(ticket_id: ticket.id).first
|
|
|
|
newer_article = create(:ticket_article, ticket_id: ticket.id)
|
|
|
|
trigger = Trigger.new(
|
|
|
|
perform: {
|
|
|
|
'notification.email' => {
|
2018-12-19 17:31:51 +00:00
|
|
|
body: '',
|
2018-05-23 10:25:11 +00:00
|
|
|
recipient: 'ticket_customer',
|
2018-12-19 17:31:51 +00:00
|
|
|
subject: ''
|
2018-05-23 10:25:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
allow(NotificationFactory::Mailer).to receive(:template).and_return('')
|
|
|
|
|
|
|
|
ticket.perform_changes(trigger.perform, 'trigger', { article_id: orig_article.id }, 1)
|
|
|
|
|
|
|
|
expect(NotificationFactory::Mailer)
|
|
|
|
.to have_received(:template)
|
|
|
|
.with(hash_including(objects: { ticket: ticket, article: orig_article }))
|
|
|
|
.at_least(:once)
|
|
|
|
|
|
|
|
expect(NotificationFactory::Mailer)
|
|
|
|
.not_to have_received(:template)
|
|
|
|
.with(hash_including(objects: { ticket: ticket, article: newer_article }))
|
|
|
|
end
|
2017-05-05 09:16:47 +00:00
|
|
|
end
|
|
|
|
|
2018-02-09 15:46:55 +00:00
|
|
|
describe '#selectors' do
|
|
|
|
|
|
|
|
# https://github.com/zammad/zammad/issues/1769
|
|
|
|
it 'does not return multiple results for a single ticket' do
|
|
|
|
source_ticket = create(:ticket)
|
|
|
|
source_ticket2 = create(:ticket)
|
|
|
|
|
|
|
|
# create some articles
|
|
|
|
create(:ticket_article, ticket_id: source_ticket.id, from: 'asdf1@blubselector.de')
|
|
|
|
create(:ticket_article, ticket_id: source_ticket.id, from: 'asdf2@blubselector.de')
|
|
|
|
create(:ticket_article, ticket_id: source_ticket.id, from: 'asdf3@blubselector.de')
|
|
|
|
create(:ticket_article, ticket_id: source_ticket2.id, from: 'asdf4@blubselector.de')
|
|
|
|
create(:ticket_article, ticket_id: source_ticket2.id, from: 'asdf5@blubselector.de')
|
|
|
|
create(:ticket_article, ticket_id: source_ticket2.id, from: 'asdf6@blubselector.de')
|
|
|
|
|
|
|
|
condition = {
|
|
|
|
'article.from' => {
|
|
|
|
operator: 'contains',
|
2018-12-19 17:31:51 +00:00
|
|
|
value: 'blubselector.de',
|
2018-02-09 15:46:55 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
ticket_count, tickets = Ticket.selectors(condition, 100, nil, 'full')
|
|
|
|
|
|
|
|
expect(ticket_count).to be == 2
|
|
|
|
expect(tickets.count).to be == 2
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-06-28 08:25:25 +00:00
|
|
|
context 'callbacks' do
|
|
|
|
|
|
|
|
describe '#reset_pending_time' do
|
|
|
|
|
|
|
|
it 'resets the pending time on state change' do
|
|
|
|
ticket = create(:ticket,
|
|
|
|
state: Ticket::State.lookup(name: 'pending reminder'),
|
|
|
|
pending_time: Time.zone.now + 2.days)
|
|
|
|
expect(ticket.pending_time).not_to be nil
|
|
|
|
|
2017-09-11 11:16:08 +00:00
|
|
|
ticket.update!(state: Ticket::State.lookup(name: 'open'))
|
2017-06-28 08:25:25 +00:00
|
|
|
expect(ticket.pending_time).to be nil
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'lets handle ActiveRecord nil as new value' do
|
|
|
|
ticket = create(:ticket)
|
|
|
|
expect do
|
2017-09-11 11:16:08 +00:00
|
|
|
ticket.update!(state: nil)
|
2017-06-28 08:25:25 +00:00
|
|
|
end.to raise_error(ActiveRecord::StatementInvalid)
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
end
|
2018-03-08 12:30:30 +00:00
|
|
|
|
|
|
|
describe '#access?' do
|
|
|
|
|
|
|
|
context 'agent' do
|
|
|
|
|
|
|
|
it 'allows owner access' do
|
|
|
|
|
|
|
|
owner = create(:agent_user)
|
|
|
|
ticket = create(:ticket, owner: owner)
|
|
|
|
|
|
|
|
expect( ticket.access?(owner, 'full') ).to be(true)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'allows group access' do
|
|
|
|
|
|
|
|
agent = create(:agent_user)
|
|
|
|
group = create(:group)
|
|
|
|
ticket = create(:ticket, group: group)
|
|
|
|
|
|
|
|
agent.group_names_access_map = {
|
|
|
|
group.name => 'full',
|
|
|
|
}
|
|
|
|
|
|
|
|
expect( ticket.access?(agent, 'full') ).to be(true)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'prevents unauthorized access' do
|
|
|
|
agent = create(:agent_user)
|
|
|
|
ticket = create(:ticket)
|
|
|
|
|
|
|
|
expect( ticket.access?(agent, 'read') ).to be(false)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'customer' do
|
|
|
|
|
|
|
|
it 'allows assigned access' do
|
|
|
|
|
|
|
|
customer = create(:customer_user)
|
|
|
|
ticket = create(:ticket, customer: customer)
|
|
|
|
|
|
|
|
expect( ticket.access?(customer, 'full') ).to be(true)
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'organization' do
|
|
|
|
|
|
|
|
it 'allows access for shared' do
|
|
|
|
|
|
|
|
organization = create(:organization)
|
|
|
|
assigned = create(:customer_user, organization: organization)
|
|
|
|
collegue = create(:customer_user, organization: organization)
|
|
|
|
ticket = create(:ticket, customer: assigned)
|
|
|
|
|
|
|
|
expect( ticket.access?(collegue, 'full') ).to be(true)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'prevents unshared access' do
|
|
|
|
|
|
|
|
organization = create(:organization, shared: false)
|
|
|
|
assigned = create(:customer_user, organization: organization)
|
|
|
|
collegue = create(:customer_user, organization: organization)
|
|
|
|
ticket = create(:ticket, customer: assigned)
|
|
|
|
|
|
|
|
expect( ticket.access?(collegue, 'full') ).to be(false)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'prevents unauthorized access' do
|
|
|
|
customer = create(:customer_user)
|
|
|
|
ticket = create(:ticket)
|
|
|
|
|
|
|
|
expect( ticket.access?(customer, 'read') ).to be(false)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2017-05-02 11:37:20 +00:00
|
|
|
end
|