Refactoring: Migrate ticket_article_store_empty to RSpec

This commit is contained in:
Ryan Lue 2019-02-10 13:24:22 +01:00 committed by Martin Edenhofer
parent 63214c9327
commit 107fd5e995
12 changed files with 466 additions and 498 deletions

View file

@ -0,0 +1,12 @@
FactoryBot.define do
factory :activity_stream do
transient do
o { Ticket.first }
end
association :type, factory: :type_lookup
activity_stream_object_id { ObjectLookup.by_name(o.class.name) }
o_id { o.id }
created_by_id { 1 }
end
end

View file

@ -1,8 +1,15 @@
FactoryBot.define do FactoryBot.define do
factory :history do factory :history do
transient do
o { Ticket.first }
end
association :history_type, factory: :'history/type' association :history_type, factory: :'history/type'
association :history_object, factory: :'history/object' o_id { o.id }
o_id { history_object.name.constantize.pluck(:id).sample }
created_by_id { 1 } created_by_id { 1 }
history_object_id do
History::Object.lookup(name: o.class.name)&.id || create(:'history/object', name: o.class.name).id
end
end end
end end

View file

@ -0,0 +1,14 @@
FactoryBot.define do
factory :'karma/activity_log', aliases: %i[karma_activity_log] do
transient do
o { Ticket.first }
end
o_id { o.id }
object_lookup_id { ObjectLookup.by_name(o.class.name) }
user_id { 1 }
activity_id { Karma::Activity.pluck(:id).sample }
score { 100 }
score_total { 100 }
end
end

View file

@ -1,7 +1,14 @@
FactoryBot.define do FactoryBot.define do
factory :link do factory :link do
transient do
from { Ticket.first }
to { Ticket.last }
end
link_type_id { Link::Type.find_by(name: 'normal').id } link_type_id { Link::Type.find_by(name: 'normal').id }
link_object_source_id { Link::Object.find_by(name: 'Ticket').id } link_object_source_id { Link::Object.find_by(name: 'Ticket').id }
link_object_target_id { Link::Object.find_by(name: 'Ticket').id } link_object_target_id { Link::Object.find_by(name: 'Ticket').id }
link_object_source_value { from.id }
link_object_target_value { to.id }
end end
end end

View file

@ -1,13 +1,17 @@
FactoryBot.define do FactoryBot.define do
factory :online_notification do factory :online_notification do
object_lookup_id { ObjectLookup.by_name('Ticket') } transient do
o_id 1 o { Ticket.first }
end
object_lookup_id { ObjectLookup.by_name(o.class.name) }
o_id { o.id }
type_lookup_id { TypeLookup.by_name('updated') } type_lookup_id { TypeLookup.by_name('updated') }
seen false seen { false }
user_id 1 user_id { 1 }
created_by_id 1 created_by_id { 1 }
updated_by_id 1 updated_by_id { 1 }
created_at Time.zone.now created_at { Time.zone.now }
updated_at Time.zone.now updated_at { Time.zone.now }
end end
end end

View file

@ -1,23 +1,12 @@
FactoryBot.define do FactoryBot.define do
factory :recent_view do factory :recent_view do
transient do transient do
type { :ticket } o { Ticket.first }
user_role { :agent } user_role { :agent }
end end
recent_view_object_id { ObjectLookup.by_name(type.to_s.camelcase) } recent_view_object_id { ObjectLookup.by_name(o.class.name) }
o_id { o.id }
# select a random record of the given object class
o_id do
random_function = case ActiveRecord::Base.connection_config[:adapter]
when 'mysql2'
'RAND'
when 'postgresql'
'RANDOM'
end
type.to_s.camelcase.constantize.order("#{random_function}()").first.id
end
# assign to an existing user, if possible # assign to an existing user, if possible
created_by_id do created_by_id do

View file

@ -1,7 +1,15 @@
FactoryBot.define do FactoryBot.define do
factory :tag do factory :tag do
tag_object_id { Tag::Object.lookup_by_name_and_create('Ticket').id } transient do
o { Ticket.first }
end
tag_item_id { Tag::Item.lookup_by_name_and_create('blub').id } tag_item_id { Tag::Item.lookup_by_name_and_create('blub').id }
created_by_id 1 o_id { o.id }
created_by_id { 1 }
tag_object_id do
Tag::Object.lookup(name: o.class.name)&.id || create(:'tag/object', name: o.class.name).id
end
end end
end end

View file

@ -0,0 +1,5 @@
FactoryBot.define do
factory :'tag/object', aliases: %i[tag_object] do
name { (ApplicationModel.descendants.select(&:any?).map(&:name) - Tag::Object.pluck(:name)).sample }
end
end

View file

@ -1,25 +1,25 @@
FactoryBot.define do FactoryBot.define do
factory :'ticket/article', aliases: %i[ticket_article] do factory :'ticket/article', aliases: %i[ticket_article] do
transient do transient do
type_name 'email' type_name { 'email' }
sender_name 'Customer' sender_name { 'Customer' }
end end
ticket ticket
from 'factory-customer-1@example.com' from { 'factory-customer-1@example.com' }
to 'factory-customer-1@example.com' to { 'factory-customer-1@example.com' }
subject 'factory article' subject { 'factory article' }
message_id 'factory@id_com_1' message_id { 'factory@id_com_1' }
body 'some message 123' body { 'some message 123' }
internal false internal { false }
sender { Ticket::Article::Sender.find_by(name: sender_name) } sender { Ticket::Article::Sender.find_by(name: sender_name) }
type { Ticket::Article::Type.find_by(name: type_name) } type { Ticket::Article::Type.find_by(name: type_name) }
updated_by_id 1 updated_by_id { 1 }
created_by_id 1 created_by_id { 1 }
factory :twitter_article do factory :twitter_article do
transient do transient do
type_name 'twitter status' type_name { 'twitter status' }
end end
association :ticket, factory: :twitter_ticket association :ticket, factory: :twitter_ticket

View file

@ -0,0 +1,17 @@
FactoryBot.define do
factory :type_lookup do
name do
# The following line ensures that the name generated by Faker
# does not conflict with any existing names in the DB.
# There's a special syntax for this
# (Faker::Verb.unique.exclude(:past_participle, [], Ticket::StateType.pluck(:name)),
# but it's not available yet in the current release of Faker (1.9.1).
Faker::Verb.unique
.instance_variable_get(:@previous_results)
.dig([:base, []])
.merge(TypeLookup.pluck(:name))
Faker::Verb.unique.base
end
end
end

View file

@ -8,229 +8,121 @@ RSpec.describe Ticket, type: :model do
it_behaves_like 'CanBeImported' it_behaves_like 'CanBeImported'
it_behaves_like 'CanLookup' it_behaves_like 'CanLookup'
subject(:ticket) { create(:ticket) }
describe 'Class methods:' do
describe '.selectors' do
# https://github.com/zammad/zammad/issues/1769
context 'when matching multiple tickets, each with multiple articles' do
let(:tickets) { create_list(:ticket, 2) }
before do
create(:ticket_article, ticket: tickets.first, from: 'asdf1@blubselector.de')
create(:ticket_article, ticket: tickets.first, from: 'asdf2@blubselector.de')
create(:ticket_article, ticket: tickets.first, from: 'asdf3@blubselector.de')
create(:ticket_article, ticket: tickets.last, from: 'asdf4@blubselector.de')
create(:ticket_article, ticket: tickets.last, from: 'asdf5@blubselector.de')
create(:ticket_article, ticket: tickets.last, from: 'asdf6@blubselector.de')
end
let(:condition) do
{
'article.from' => {
operator: 'contains',
value: 'blubselector.de',
},
}
end
it 'returns a list of unique tickets (i.e., no duplicates)' do
expect(Ticket.selectors(condition, 100, nil, 'full'))
.to match_array([2, tickets])
end
end
end
end
describe 'Instance methods:' do
describe '#merge_to' do describe '#merge_to' do
let(:target_ticket) { create(:ticket) }
context 'when source ticket has Links' do
let(:linked_tickets) { create_list(:ticket, 3) }
let(:links) { linked_tickets.map { |l| create(:link, from: ticket, to: l) } }
it 'reassigns all links to the target ticket after merge' do it 'reassigns all links to the target ticket after merge' do
source_ticket = create(:ticket) expect { ticket.merge_to(ticket_id: target_ticket.id, user_id: 1) }
target_ticket = create(:ticket) .to change { links.each(&:reload).map(&:link_object_source_value) }
.to(Array.new(3) { target_ticket.id })
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(
link_object: 'Ticket',
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
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)
expect do
result = target_ticket.merge_to(
ticket_id: source_ticket.id,
user_id: 1,
)
end.to raise_error('ticket already merged, no merge into merged ticket possible')
end
it 'prevents merging ticket in it self' do
source_ticket = create(:ticket)
expect do
result = source_ticket.merge_to(
ticket_id: source_ticket.id,
user_id: 1,
)
end.to raise_error('Can\'t merge ticket with it self!')
end
end
describe '.create' do
it 'handles NULL byte in title' do
expect(create(:ticket, title: "some title \u0000 123"))
.to be_persisted
end end
end end
describe '#destroy' do context 'when attempting to cross-merge (i.e., to merge B → A after merging A → B)' do
before { target_ticket.merge_to(ticket_id: ticket.id, user_id: 1) }
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'),
o_id: source_ticket.id,
)
links = Link.list(
link_object: 'Ticket',
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'),
o_id: source_ticket.id,
)
online_notifications = OnlineNotification.where(
object_lookup_id: ObjectLookup.by_name('Ticket'),
o_id: source_ticket.id,
)
recent_views = OnlineNotification.where(
object_lookup_id: ObjectLookup.by_name('Ticket'),
o_id: source_ticket.id,
)
tags = Tag.tag_list(
object: 'Ticket',
o_id: source_ticket.id,
)
# 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'),
o_id: source_ticket.id,
)
links = Link.list(
link_object: 'Ticket',
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'),
o_id: source_ticket.id,
)
online_notifications = OnlineNotification.where(
object_lookup_id: ObjectLookup.by_name('Ticket'),
o_id: source_ticket.id,
)
recent_views = OnlineNotification.where(
object_lookup_id: ObjectLookup.by_name('Ticket'),
o_id: source_ticket.id,
)
tags = Tag.tag_list(
object: 'Ticket',
o_id: source_ticket.id,
)
# 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
it 'raises an error' do
expect { ticket.merge_to(ticket_id: target_ticket.id, user_id: 1) }
.to raise_error('ticket already merged, no merge into merged ticket possible')
end
end end
context 'when attempting to self-merge (i.e., to merge A → A)' do
it 'raises an error' do
expect { ticket.merge_to(ticket_id: ticket.id, user_id: 1) }
.to raise_error("Can't merge ticket with it self!")
end
end
end end
describe '#perform_changes' do describe '#perform_changes' do
it 'performs a ticket state change on a ticket' do
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
it 'performs a ticket deletion on a ticket' do
source_ticket = create(:ticket)
changes = {
'ticket.state_id' => { 'value' => Ticket::State.lookup(name: 'closed').id.to_s },
'ticket.action' => { 'value' => 'delete' },
}
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
# Regression test for https://github.com/zammad/zammad/issues/2001 # Regression test for https://github.com/zammad/zammad/issues/2001
it 'does not modify its arguments' do describe 'argument handling' do
trigger = Trigger.new( let(:perform) do
perform: { {
'notification.email' => { 'notification.email' => {
body: "Hello \#{ticket.customer.firstname} \#{ticket.customer.lastname},", body: "Hello \#{ticket.customer.firstname} \#{ticket.customer.lastname},",
recipient: %w[article_last_sender ticket_owner ticket_customer ticket_agents], recipient: %w[article_last_sender ticket_owner ticket_customer ticket_agents],
subject: "Autoclose (\#{ticket.title})" subject: "Autoclose (\#{ticket.title})"
} }
} }
)
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 end
it 'does not mutate contents of "perform" hash' do
expect { ticket.perform_changes(perform, '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 { ticket.perform_changes(perform, 'trigger', ticket, User.first) }
.to change { ticket.reload.state.name }.to('closed')
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 { ticket.perform_changes(perform, 'trigger', ticket, User.first) }
.to change { ticket.destroyed? }.to(true)
end
end
context 'with a "notification.email" trigger' do
# Regression test for https://github.com/zammad/zammad/issues/1543 # Regression test for https://github.com/zammad/zammad/issues/1543
# #
# If a new article fires an email notification trigger, # If a new article fires an email notification trigger,
@ -240,162 +132,249 @@ RSpec.describe Ticket, type: :model do
# not the newest one. # not the newest one.
# #
# (This occurs whenever one action fires multiple email notification triggers.) # (This occurs whenever one action fires multiple email notification triggers.)
it 'passes the correct article to NotificationFactory::Mailer' do context 'when two articles are created before the trigger fires once (race condition)' do
# required by Ticket#perform_changes for email notifications let!(:article) { create(:ticket_article, ticket: ticket) }
Group.first.update(email_address: create(:email_address)) let!(:new_article) { create(:ticket_article, ticket: ticket) }
ticket = Ticket.first let(:trigger) do
orig_article = Ticket::Article.where(ticket_id: ticket.id).first build(:trigger,
newer_article = create(:ticket_article, ticket_id: ticket.id)
trigger = Trigger.new(
perform: { perform: {
'notification.email' => { 'notification.email' => {
body: '', body: '',
recipient: 'ticket_customer', recipient: 'ticket_customer',
subject: '' subject: ''
} }
} })
) end
allow(NotificationFactory::Mailer).to receive(:template).and_return('') # required by Ticket#perform_changes for email notifications
before { article.ticket.group.update(email_address: create(:email_address)) }
ticket.perform_changes(trigger.perform, 'trigger', { article_id: orig_article.id }, 1)
it 'passes the first article to NotificationFactory::Mailer' do
expect(NotificationFactory::Mailer) expect(NotificationFactory::Mailer)
.to have_received(:template) .to receive(:template)
.with(hash_including(objects: { ticket: ticket, article: orig_article })) .with(hash_including(objects: { ticket: ticket, article: article }))
.at_least(:once) .at_least(:once)
.and_call_original
expect(NotificationFactory::Mailer) expect(NotificationFactory::Mailer)
.not_to have_received(:template) .not_to receive(:template)
.with(hash_including(objects: { ticket: ticket, article: newer_article })) .with(hash_including(objects: { ticket: ticket, article: new_article }))
ticket.perform_changes(trigger.perform, 'trigger', { article_id: article.id }, 1)
end end
end end
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',
value: 'blubselector.de',
},
}
ticket_count, tickets = Ticket.selectors(condition, 100, nil, 'full')
expect(ticket_count).to be == 2
expect(tickets.count).to be == 2
end
end
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
ticket.update!(state: Ticket::State.lookup(name: 'open'))
expect(ticket.pending_time).to be nil
end
it 'lets handle ActiveRecord nil as new value' do
ticket = create(:ticket)
expect do
ticket.update!(state: nil)
end.to raise_error(ActiveRecord::StatementInvalid)
end
end end
end end
describe '#access?' do describe '#access?' do
context 'when given tickets owner' do
context 'agent' do it 'returns true for both "read" and "full" privileges' do
expect(ticket.access?(ticket.owner, 'read')).to be(true)
it 'allows owner access' do expect(ticket.access?(ticket.owner, 'full')).to be(true)
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
end end
context 'customer' do context 'when given the tickets customer' do
it 'returns true for both "read" and "full" privileges' do
it 'allows assigned access' do expect(ticket.access?(ticket.customer, 'read')).to be(true)
expect(ticket.access?(ticket.customer, 'full')).to be(true)
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
end end
it 'prevents unauthorized access' do context 'when given a user that is neither owner nor customer' do
customer = create(:customer_user) let(:user) { create(:agent_user) }
ticket = create(:ticket)
expect( ticket.access?(customer, 'read') ).to be(false) it 'returns false for both "read" and "full" privileges' do
expect(ticket.access?(user, 'read')).to be(false)
expect(ticket.access?(user, 'full')).to be(false)
end
context 'but the user is an agent with full access to tickets group' do
before { user.group_names_access_map = { ticket.group.name => 'full' } }
it 'returns true for both "read" and "full" privileges' do
expect(ticket.access?(user, 'read')).to be(true)
expect(ticket.access?(user, 'full')).to be(true)
end
end
context 'but the user is a customer from the same organization as tickets customer' do
subject(:ticket) { create(:ticket, customer: customer) }
let(:customer) { create(:customer_user, organization: create(:organization)) }
let(:colleague) { create(:customer_user, organization: customer.organization) }
context 'and organization.shared is true (default)' do
it 'returns true for both "read" and "full" privileges' do
expect(ticket.access?(colleague, 'read')).to be(true)
expect(ticket.access?(colleague, 'full')).to be(true)
end
end
context 'but organization.shared is false' do
before { customer.organization.update(shared: false) }
it 'returns false for both "read" and "full" privileges' do
expect(ticket.access?(colleague, 'read')).to be(false)
expect(ticket.access?(colleague, 'full')).to be(false)
end
end
end
end
end
end
describe 'Attributes:' do
describe '#pending_time' do
subject(:ticket) { create(:ticket, pending_time: Time.zone.now + 2.days) }
context 'when #state is updated to any non-"pending" value' do
it 'is reset to nil' do
expect { ticket.update!(state: Ticket::State.lookup(name: 'open')) }
.to change { ticket.pending_time }.to(nil)
end
end
# Regression test for commit 92f227786f298bad1ccaf92d4478a7062ea6a49f
context 'when #state is updated to nil (violating DB NOT NULL constraint)' do
it 'does not prematurely raise within the callback (#reset_pending_time)' do
expect { ticket.update!(state: nil) }
.to raise_error(ActiveRecord::StatementInvalid)
end
end
end
end
describe 'Callbacks & Observers -' do
describe 'NULL byte handling (via ChecksAttributeValuesAndLength concern):' do
it 'removes them from title on creation, if necessary (postgres doesnt like them)' do
expect { create(:ticket, title: "some title \u0000 123") }
.not_to raise_error
end
end
describe 'Association & attachment management:' do
it 'deletes all related ActivityStreams on destroy' do
create_list(:activity_stream, 3, o: ticket)
expect { ticket.destroy }
.to change { ActivityStream.exists?(activity_stream_object_id: ObjectLookup.by_name('Ticket'), o_id: ticket.id) }
.to(false)
end
it 'deletes all related Links on destroy' do
create(:link, from: ticket, to: create(:ticket))
create(:link, from: create(:ticket), to: ticket)
create(:link, from: ticket, to: create(:ticket))
expect { ticket.destroy }
.to change { Link.where('link_object_source_value = :id OR link_object_target_value = :id', id: ticket.id).any? }
.to(false)
end
it 'deletes all related Articles on destroy' do
create_list(:ticket_article, 3, ticket: ticket)
expect { ticket.destroy }
.to change { Ticket::Article.exists?(ticket: ticket) }
.to(false)
end
it 'deletes all related OnlineNotifications on destroy' do
create_list(:online_notification, 3, o: ticket)
expect { ticket.destroy }
.to change { OnlineNotification.where(object_lookup_id: ObjectLookup.by_name('Ticket'), o_id: ticket.id).any? }
.to(false)
end
it 'deletes all related Tags on destroy' do
create_list(:tag, 3, o: ticket)
expect { ticket.destroy }
.to change { Tag.exists?(tag_object_id: Tag::Object.lookup(name: 'Ticket').id, o_id: ticket.id) }
.to(false)
end
it 'deletes all related Histories on destroy' do
create_list(:history, 3, o: ticket)
expect { ticket.destroy }
.to change { History.exists?(history_object_id: History::Object.lookup(name: 'Ticket').id, o_id: ticket.id) }
.to(false)
end
it 'deletes all related Karma::ActivityLogs on destroy' do
create_list(:'karma/activity_log', 3, o: ticket)
expect { ticket.destroy }
.to change { Karma::ActivityLog.exists?(object_lookup_id: ObjectLookup.by_name('Ticket'), o_id: ticket.id) }
.to(false)
end
it 'deletes all related RecentViews on destroy' do
create_list(:recent_view, 3, o: ticket)
expect { ticket.destroy }
.to change { RecentView.exists?(recent_view_object_id: ObjectLookup.by_name('Ticket'), o_id: ticket.id) }
.to(false)
end
context 'when ticket is generated from email (with attachments)' do
subject(:ticket) { Channel::EmailParser.new.process({}, raw_email).first }
let(:raw_email) { File.read(Rails.root.join('test', 'data', 'mail', 'mail001.box')) }
it 'adds attachments to the Store{::File,::Provider::DB} tables' do
expect { ticket }
.to change { Store.count }.by(2)
.and change { Store::File.count }.by(2)
.and change { Store::Provider::DB.count }.by(2)
end
context 'and subsequently destroyed' do
it 'deletes all related attachments' do
ticket # create ticket
expect { ticket.destroy }
.to change { Store.count }.by(-2)
.and change { Store::File.count }.by(-2)
.and change { Store::Provider::DB.count }.by(-2)
end
end
context 'and a duplicate ticket is generated from the same email' do
before { ticket } # create ticket
let(:duplicate) { Channel::EmailParser.new.process({}, raw_email).first }
it 'adds duplicate attachments to the Store table only' do
expect { duplicate }
.to change { Store.count }.by(2)
.and change { Store::File.count }.by(0)
.and change { Store::Provider::DB.count }.by(0)
end
context 'when only the duplicate ticket is destroyed' do
it 'deletes only the duplicate attachments' do
duplicate # create ticket
expect { duplicate.destroy }
.to change { Store.count }.by(-2)
.and change { Store::File.count }.by(0)
.and change { Store::Provider::DB.count }.by(0)
end
end
context 'when only the duplicate ticket is destroyed' do
it 'deletes all related attachments' do
duplicate.destroy
expect { ticket.destroy }
.to change { Store.count }.by(-2)
.and change { Store::File.count }.by(-2)
.and change { Store::Provider::DB.count }.by(-2)
end
end
end
end end
end end
end end

View file

@ -1,74 +0,0 @@
require 'test_helper'
class TicketArticleStoreEmpty < ActiveSupport::TestCase
test 'check if attachments are deleted after ticket is deleted' do
current_count = Store.count
current_file_count = Store::File.count
current_backend_count = Store::Provider::DB.count
email_raw_string = File.read(Rails.root.join('test', 'data', 'mail', 'mail001.box'))
ticket, article, user, mail = Channel::EmailParser.new.process({}, email_raw_string)
next_count = Store.count
next_file_count = Store::File.count
next_backend_count = Store::Provider::DB.count
assert_equal(current_count, next_count - 2)
assert_equal(current_file_count, next_file_count - 2)
assert_equal(current_backend_count, next_backend_count - 2)
ticket.destroy!
after_count = Store.count
after_file_count = Store::File.count
after_backend_count = Store::Provider::DB.count
assert_equal(current_count, after_count)
assert_equal(current_file_count, after_file_count)
assert_equal(current_backend_count, after_backend_count)
end
test 'check if attachments are deleted after ticket same ticket 2 times is deleted' do
current_count = Store.count
current_file_count = Store::File.count
current_backend_count = Store::Provider::DB.count
email_raw_string = File.read(Rails.root.join('test', 'data', 'mail', 'mail001.box'))
ticket1, article1, user1, mail1 = Channel::EmailParser.new.process({}, email_raw_string)
ticket2, article2, user2, mail2 = Channel::EmailParser.new.process({}, email_raw_string)
next_count = Store.count
next_file_count = Store::File.count
next_backend_count = Store::Provider::DB.count
assert_equal(current_count, next_count - 4)
assert_equal(current_file_count, next_file_count - 2)
assert_equal(current_backend_count, next_backend_count - 2)
ticket1.destroy!
next_count = Store.count
next_file_count = Store::File.count
next_backend_count = Store::Provider::DB.count
assert_equal(current_count, next_count - 2)
assert_equal(current_file_count, next_file_count - 2)
assert_equal(current_backend_count, next_backend_count - 2)
ticket2.destroy!
after_count = Store.count
after_file_count = Store::File.count
after_backend_count = Store::Provider::DB.count
assert_equal(current_count, after_count)
assert_equal(current_file_count, after_file_count)
assert_equal(current_backend_count, after_backend_count)
end
end