Refactoring: Migrate *_assets_test.rb to RSpec

This commit is contained in:
Ryan Lue 2019-02-26 12:00:46 +01:00 committed by Thorsten Eckel
parent e48256c130
commit ca73fdd9b7
19 changed files with 202 additions and 549 deletions

View file

@ -1,30 +1,28 @@
FactoryBot.define do
sequence :test_factory_name do |n|
"Test Overview #{n}"
end
end
FactoryBot.define do
factory :overview do
name { generate(:test_factory_name) }
prio 1
role_ids { [ Role.find_by(name: 'Customer').id, Role.find_by(name: 'Agent').id, Role.find_by(name: 'Admin').id ] }
out_of_office true
sequence(:name) { |n| "Test Overview #{n}" }
prio { 1 }
role_ids { Role.where(name: %w[Customer Agent Admin]).pluck(:id) }
out_of_office { true }
updated_by_id { 1 }
created_by_id { 1 }
condition do
{
'ticket.state_id' => {
operator: 'is',
value: [ Ticket::State.lookup(name: 'new').id, Ticket::State.lookup(name: 'open').id ],
value: Ticket::State.where(name: %w[new open]).pluck(:id),
},
}
end
order do
{
by: 'created_at',
direction: 'DESC',
}
end
view do
{
d: %w[title customer state created_at],
@ -33,7 +31,5 @@ FactoryBot.define do
view_mode_default: 's',
}
end
updated_by_id 1
created_by_id 1
end
end

View file

@ -1,11 +1,11 @@
FactoryBot.define do
factory :sla do
sequence(:name) { |n| "SLA #{n}" }
first_response_time nil
update_time nil
solution_time nil
calendar { create(:calendar) }
condition do
calendar
sequence(:name) { |n| "SLA #{n}" }
created_by_id { 1 }
updated_by_id { 1 }
condition do
{
'ticket.state_id' => {
operator: 'is',
@ -13,7 +13,5 @@ FactoryBot.define do
},
}
end
created_by_id 1
updated_by_id 1
end
end

View file

@ -1,10 +1,10 @@
FactoryBot.define do
factory :trigger do
sequence(:name) { |n| "Test trigger #{n}" }
condition { { 'ticket.state_id' => { 'operator' => 'is not', 'value' => 4 } } }
perform { { 'ticket.state_id' => { 'value' => 4 } } }
active true
created_by_id 1
updated_by_id 1
condition { { 'ticket.state_id' => { 'operator' => 'is not', 'value' => 4 } } }
perform { { 'ticket.state_id' => { 'value' => 4 } } }
active { true }
created_by_id { 1 }
updated_by_id { 1 }
end
end

View file

@ -0,0 +1,97 @@
RSpec.shared_examples 'ApplicationModel::CanAssets' do |associations: [], selectors: [], own_attributes: true|
subject { create(described_class.name.underscore, updated_by_id: admin.id) }
let(:admin) { create(:admin_user) }
describe '#assets (for supplying model data to front-end framework)' do
shared_examples 'own asset attributes' do
it 'returns a hash with own asset attributes' do
expect(subject.assets({}).dig(described_class.to_app_model))
.to include(subject.id => hash_including(subject.attributes_with_association_ids))
end
end
include_examples 'own asset attributes' if own_attributes
describe 'for created_by & updated_by users' do
let(:users) { User.where(id: subject.attributes.slice('created_by_id', 'updated_by_id').values) }
let(:users_assets) { users.reduce({}) { |assets_hash, user| user.assets(assets_hash) } }
it 'returns a hash with their asset attributes' do
expect(subject.assets({})[:User]).to include(users_assets[:User])
end
end
context 'when given a non-empty hash' do
let(:hash) { { described_class.to_app_model => { foo: 'bar' } } }
it 'deep-merges assets into it, in place' do
expect { subject.assets(hash) }
.to change { hash }
.to(hash.deep_merge(subject.assets({})))
end
end
Array(associations).each do |association|
describe "for ##{association} association" do
let(:reflection) { described_class.reflect_on_association(association) }
shared_examples 'single association' do
subject { create(described_class.name.underscore, association => single) }
let(:single) { create(reflection.class_name.underscore) }
it 'returns a hash with its asset attributes' do
expect(subject.assets({})).to include(single.assets({}))
end
context 'after association has been modified' do
it 'does not use a cached value' do
subject.assets({})
single.update(updated_by_id: User.last.id)
expect(subject.assets({}).dig(reflection.klass.to_app_model, single.id))
.to include(single.attributes_with_association_ids)
end
end
end
shared_examples 'collection association' do
subject { create(described_class.name.underscore, association => collection) }
let(:collection) { create_list(reflection.class_name.underscore, 5) }
let(:collection_assets) { collection.reduce({}) { |assets_hash, single| single.assets(assets_hash) } }
it 'returns a hash with their asset attributes' do
expect(subject.assets({})).to include(collection_assets)
end
context 'after association has been modified' do
it 'does not use a cached value' do
subject.assets({})
collection.first.update(updated_by_id: User.last.id)
expect(subject.assets({}).dig(reflection.klass.to_app_model, collection.first.id))
.to include(collection.first.attributes_with_association_ids)
end
end
end
if described_class.reflect_on_association(association).macro.in?(%i[has_one belongs_to])
include_examples 'single association'
else
include_examples 'collection association'
end
end
end
Array(selectors).each do |s|
subject { create(described_class.name.underscore, s => selector) }
let(:selector) { { 'ticket.priority_id' => { operator: 'is', value: [1, 2] } } }
let(:priorities_assets) { Ticket::Priority.first(2).reduce({}) { |asset_hash, priority| priority.assets(asset_hash) } }
describe "for objects referenced in ##{s}" do
it 'returns a hash with their asset attributes' do
expect(subject.assets({})).to include(priorities_assets)
end
end
end
end
end

View file

@ -0,0 +1,29 @@
RSpec.shared_examples 'ApplicationModel::CanAssociations' do
subject { create(described_class.name.underscore) }
describe '#attributes_with_association_ids (for supplying model data to front-end framework)' do
describe 'caching' do
let(:cache_key) { "#{described_class}::aws::#{subject.id}" }
context 'with empty cache' do
it 'stores the computed value in the cache' do
expect { subject.attributes_with_association_ids }
.to change { Rails.cache.read(cache_key) }
end
end
context 'with stored value in cache' do
before { Rails.cache.write(cache_key, { foo: 'bar' }) }
it 'returns the cached value' do
expect(subject.attributes_with_association_ids).to include(foo: 'bar')
end
it 'does not modify the cached value' do
expect { subject.attributes_with_association_ids }
.not_to change { Rails.cache.read(cache_key) }
end
end
end
end
end

View file

@ -1,5 +1,9 @@
require 'models/application_model/can_assets_examples'
require 'models/application_model/can_associations_examples'
require 'models/application_model/checks_import_examples'
RSpec.shared_examples 'ApplicationModel' do
RSpec.shared_examples 'ApplicationModel' do |options = {}|
include_examples 'ApplicationModel::CanAssets', options[:can_assets]
include_examples 'ApplicationModel::CanAssociations'
include_examples 'ApplicationModel::ChecksImport'
end

View file

@ -3,6 +3,6 @@ require 'models/application_model_examples'
require 'models/concerns/can_be_imported_examples'
RSpec.describe History, type: :model do
it_behaves_like 'ApplicationModel'
it_behaves_like 'ApplicationModel', can_assets: { own_attributes: false }
it_behaves_like 'CanBeImported'
end

6
spec/models/job_spec.rb Normal file
View file

@ -0,0 +1,6 @@
require 'rails_helper'
require 'models/application_model_examples'
RSpec.describe Job, type: :model do
it_behaves_like 'ApplicationModel', can_assets: { selectors: %i[condition perform] }
end

View file

@ -1,9 +1,11 @@
require 'rails_helper'
require 'models/application_model_examples'
require 'models/concerns/can_lookup_examples'
require 'models/concerns/has_search_index_backend_examples'
require 'models/concerns/has_xss_sanitized_note_examples'
RSpec.describe Organization, type: :model do
it_behaves_like 'ApplicationModel', can_assets: { associations: :members }
it_behaves_like 'CanLookup'
it_behaves_like 'HasSearchIndexBackend', indexed_factory: :organization
it_behaves_like 'HasXssSanitizedNote', model_factory: :organization

View file

@ -1,6 +1,7 @@
require 'rails_helper'
RSpec.describe Overview do
RSpec.describe Overview, type: :model do
it_behaves_like 'ApplicationModel', can_assets: { associations: :users, selectors: :condition }
context 'link generation' do

6
spec/models/sla_spec.rb Normal file
View file

@ -0,0 +1,6 @@
require 'rails_helper'
require 'models/application_model_examples'
RSpec.describe Sla, type: :model do
it_behaves_like 'ApplicationModel', can_assets: { associations: :calendar, selectors: :condition }
end

View file

@ -0,0 +1,20 @@
require 'rails_helper'
require 'models/application_model_examples'
RSpec.describe Trigger, type: :model do
it_behaves_like 'ApplicationModel', can_assets: { selectors: %i[condition perform] }
subject(:trigger) { create(:trigger) }
describe '#assets (for supplying model data to front-end framework)' do
subject(:trigger) { create(:trigger, condition: condition, perform: perform) }
let(:condition) { { 'ticket.state_id' => { operator: 'is', value: 1 } } }
let(:perform) { { 'ticket.priority_id' => { value: 1 } } }
it 'returns a hash with asset attributes for objects referenced in #condition and #perform' do
expect(trigger.assets({}))
.to include(Ticket::State.first.assets({}))
.and include(Ticket::Priority.first.assets({}))
end
end
end

View file

@ -8,7 +8,7 @@ require 'models/concerns/can_be_imported_examples'
require 'models/concerns/can_lookup_examples'
RSpec.describe User, type: :model do
it_behaves_like 'ApplicationModel'
it_behaves_like 'ApplicationModel', can_assets: { associations: :organization }
it_behaves_like 'HasGroups', group_access_factory: :agent_user
it_behaves_like 'HasRoles', group_access_factory: :agent_user
it_behaves_like 'HasXssSanitizedNote', model_factory: :user

View file

@ -1,79 +0,0 @@
require 'test_helper'
class JobAssetsTest < ActiveSupport::TestCase
test 'assets' do
UserInfo.current_user_id = 1
roles = Role.where(name: %w[Customer])
user1 = User.create_or_update(
login: 'assets_job1@example.org',
firstname: 'assets_job1',
lastname: 'assets_job1',
email: 'assets_job1@example.org',
password: 'some_pass',
active: true,
roles: roles,
)
user2 = User.create_or_update(
login: 'assets_job2@example.org',
firstname: 'assets_job2',
lastname: 'assets_job2',
email: 'assets_job2@example.org',
password: 'some_pass',
active: true,
roles: roles,
)
user3 = User.create_or_update(
login: 'assets_job3@example.org',
firstname: 'assets_job3',
lastname: 'assets_job3',
email: 'assets_job3@example.org',
password: 'some_pass',
active: true,
roles: roles,
)
ticket_state1 = Ticket::State.find_by(name: 'new')
ticket_state2 = Ticket::State.find_by(name: 'open')
ticket_priority2 = Ticket::Priority.find_by(name: '2 normal')
job = Job.create_or_update(
name: 'my job',
timeplan: {
mon: true,
},
condition: {
'ticket.state_id' => {
operator: 'is',
value: [ ticket_state1.id, ticket_state2.id ],
},
'ticket.owner_id' => {
operator: 'is',
pre_condition: 'specific',
value: user1.id,
value_completion: 'John Smith <john.smith@example.com>'
},
},
perform: {
'ticket.priority_id' => {
value: ticket_priority2.id,
},
'ticket.owner_id' => {
pre_condition: 'specific',
value: user2.id,
value_completion: 'metest123@znuny.com <metest123@znuny.com>'
},
},
disable_notification: true,
)
assets = job.assets({})
assert(assets[:User][user1.id])
assert(assets[:User][user2.id])
assert_not(assets[:User][user3.id])
assert(assets[:TicketState][ticket_state1.id])
assert(assets[:TicketState][ticket_state2.id])
assert(assets[:TicketPriority][ticket_priority2.id])
end
end

View file

@ -1,158 +0,0 @@
require 'test_helper'
class OrganizationAssetsTest < ActiveSupport::TestCase
test 'assets' do
roles = Role.where( name: %w[Agent Admin] )
admin1 = User.create_or_update(
login: 'admin1@example.org',
firstname: 'admin1',
lastname: 'admin1',
email: 'admin1@example.org',
password: 'some_pass',
active: true,
updated_by_id: 1,
created_by_id: 1,
roles: roles,
)
roles = Role.where( name: %w[Customer] )
org = Organization.create_or_update(
name: 'some customer org',
updated_by_id: admin1.id,
created_by_id: 1,
)
user1 = User.create_or_update(
login: 'assets1@example.org',
firstname: 'assets1',
lastname: 'assets1',
email: 'assets1@example.org',
password: 'some_pass',
active: true,
updated_by_id: 1,
created_by_id: 1,
organization_id: org.id,
roles: roles,
)
user2 = User.create_or_update(
login: 'assets2@example.org',
firstname: 'assets2',
lastname: 'assets2',
email: 'assets2@example.org',
password: 'some_pass',
active: true,
updated_by_id: 1,
created_by_id: 1,
organization_id: org.id,
roles: roles,
)
user3 = User.create_or_update(
login: 'assets3@example.org',
firstname: 'assets3',
lastname: 'assets3',
email: 'assets3@example.org',
password: 'some_pass',
active: true,
updated_by_id: user1.id,
created_by_id: user2.id,
roles: roles,
)
org = Organization.find(org.id)
assets = org.assets({})
attributes = org.attributes_with_association_ids
attributes.delete('user_ids')
assert( diff(attributes, assets[:Organization][org.id]), 'check assets' )
admin1 = User.find(admin1.id)
attributes = admin1.attributes_with_association_ids
attributes['accounts'] = {}
attributes.delete('password')
attributes.delete('token_ids')
attributes.delete('authorization_ids')
assert( diff(attributes, assets[:User][admin1.id]), 'check assets' )
user1 = User.find(user1.id)
attributes = user1.attributes_with_association_ids
attributes['accounts'] = {}
attributes.delete('password')
attributes.delete('token_ids')
attributes.delete('authorization_ids')
assert( diff(attributes, assets[:User][user1.id]), 'check assets' )
user2 = User.find(user2.id)
attributes = user2.attributes_with_association_ids
attributes['accounts'] = {}
attributes.delete('password')
attributes.delete('token_ids')
attributes.delete('authorization_ids')
assert( diff(attributes, assets[:User][user2.id]), 'check assets' )
user3 = User.find(user3.id)
attributes = user3.attributes_with_association_ids
attributes['accounts'] = {}
attributes.delete('password')
attributes.delete('token_ids')
attributes.delete('authorization_ids')
assert_nil( assets[:User][user3.id], 'check assets' )
# touch user 2, check if org has changed
travel 2.seconds
user_new_2 = User.find(user2.id)
user_new_2.lastname = 'assets2'
user_new_2.save!
org_new = Organization.find(org.id)
attributes = org_new.attributes_with_association_ids
attributes.delete('user_ids')
assert_not(diff(attributes, assets[:Organization][org_new.id]), 'check assets' )
attributes = user_new_2.attributes_with_association_ids
attributes['accounts'] = {}
attributes.delete('password')
attributes.delete('token_ids')
attributes.delete('authorization_ids')
assert( diff(attributes, assets[:User][user_new_2.id]), 'check assets' )
# check new assets lookup
assets = org_new.assets({})
attributes = org_new.attributes_with_association_ids
attributes.delete('user_ids')
assert( diff(attributes, assets[:Organization][org_new.id]), 'check assets' )
attributes = user_new_2.attributes_with_association_ids
attributes['accounts'] = {}
attributes.delete('password')
attributes.delete('token_ids')
attributes.delete('authorization_ids')
assert( diff(attributes, assets[:User][user_new_2.id]), 'check assets' )
travel_back
user3.destroy!
user2.destroy!
user1.destroy!
org.destroy!
assert_not(Organization.find_by(id: org_new.id))
end
def diff(object1, object2)
return true if object1 == object2
%w[updated_at created_at].each do |item|
if object1[item]
object1[item] = object1[item].to_s
end
if object2[item]
object2[item] = object2[item].to_s
end
end
return true if (object1.to_a - object2.to_a).blank?
#puts "ERROR: difference \n1: #{object1.inspect}\n2: #{object2.inspect}\ndiff: #{(object1.to_a - object2.to_a).inspect}"
false
end
end

View file

@ -1,135 +0,0 @@
require 'test_helper'
class OverviewAssetsTest < ActiveSupport::TestCase
test 'assets' do
UserInfo.current_user_id = 1
roles = Role.where(name: %w[Customer])
user1 = User.create_or_update(
login: 'assets_overview1@example.org',
firstname: 'assets_overview1',
lastname: 'assets_overview1',
email: 'assets_overview1@example.org',
password: 'some_pass',
active: true,
roles: roles,
)
user2 = User.create_or_update(
login: 'assets_overview2@example.org',
firstname: 'assets_overview2',
lastname: 'assets_overview2',
email: 'assets_overview2@example.org',
password: 'some_pass',
active: true,
roles: roles,
)
user3 = User.create_or_update(
login: 'assets_overview3@example.org',
firstname: 'assets_overview3',
lastname: 'assets_overview3',
email: 'assets_overview3@example.org',
password: 'some_pass',
active: true,
roles: roles,
)
user4 = User.create_or_update(
login: 'assets_overview4@example.org',
firstname: 'assets_overview4',
lastname: 'assets_overview4',
email: 'assets_overview4@example.org',
password: 'some_pass',
active: true,
roles: roles,
)
user5 = User.create_or_update(
login: 'assets_overview5@example.org',
firstname: 'assets_overview5',
lastname: 'assets_overview5',
email: 'assets_overview5@example.org',
password: 'some_pass',
active: true,
roles: roles,
)
ticket_state1 = Ticket::State.find_by(name: 'new')
ticket_state2 = Ticket::State.find_by(name: 'open')
overview_role = Role.find_by(name: 'Agent')
overview = Overview.create_or_update(
name: 'my asset test',
link: 'my_asset_test',
prio: 1000,
role_ids: [overview_role.id],
user_ids: [user4.id, user5.id],
condition: {
'ticket.state_id' => {
operator: 'is',
value: [ ticket_state1.id, ticket_state2.id ],
},
'ticket.owner_id' => {
operator: 'is',
pre_condition: 'specific',
value: user1.id,
value_completion: 'John Smith <john.smith@example.com>'
},
},
order: {
by: 'created_at',
direction: 'ASC',
},
view: {
d: %w[title customer group created_at],
s: %w[title customer group created_at],
m: %w[number title customer group created_at],
view_mode_default: 's',
},
)
assets = overview.assets({})
assert(assets[:User][user1.id])
assert_not(assets[:User][user2.id])
assert_not(assets[:User][user3.id])
assert(assets[:User][user4.id])
assert(assets[:User][user5.id])
assert(assets[:TicketState][ticket_state1.id])
assert(assets[:TicketState][ticket_state2.id])
overview = Overview.create_or_update(
name: 'my asset test',
link: 'my_asset_test',
prio: 1000,
role_ids: [overview_role.id],
user_ids: [user4.id],
condition: {
'ticket.state_id' => {
operator: 'is',
value: ticket_state1.id,
},
'ticket.owner_id' => {
operator: 'is',
pre_condition: 'specific',
value: [user1.id, user2.id],
},
},
order: {
by: 'created_at',
direction: 'ASC',
},
view: {
d: %w[title customer group created_at],
s: %w[title customer group created_at],
m: %w[number title customer group created_at],
view_mode_default: 's',
},
)
assets = overview.assets({})
assert(assets[:User][user1.id])
assert(assets[:User][user2.id])
assert_not(assets[:User][user3.id])
assert(assets[:User][user4.id])
assert_not(assets[:User][user5.id])
assert(assets[:TicketState][ticket_state1.id])
assert_not(assets[:TicketState][ticket_state2.id])
overview.destroy!
end
end

View file

@ -1,70 +0,0 @@
require 'test_helper'
class SlaAssetsTest < ActiveSupport::TestCase
test 'assets' do
UserInfo.current_user_id = 1
roles = Role.where(name: %w[Customer])
user1 = User.create_or_update(
login: 'assets_sla1@example.org',
firstname: 'assets_sla1',
lastname: 'assets_sla1',
email: 'assets_sla1@example.org',
password: 'some_pass',
active: true,
roles: roles,
)
user2 = User.create_or_update(
login: 'assets_sla2@example.org',
firstname: 'assets_sla2',
lastname: 'assets_sla2',
email: 'assets_sla2@example.org',
password: 'some_pass',
active: true,
roles: roles,
)
calendar1 = Calendar.create_or_update(
name: 'US 1',
timezone: 'America/Los_Angeles',
business_hours: {
mon: { '09:00' => '17:00' },
tue: { '09:00' => '17:00' },
wed: { '09:00' => '17:00' },
thu: { '09:00' => '17:00' },
fri: { '09:00' => '17:00' }
},
default: true,
ical_url: nil,
updated_by_id: 1,
created_by_id: 1,
)
ticket_state1 = Ticket::State.find_by(name: 'new')
ticket_state2 = Ticket::State.find_by(name: 'open')
sla = Sla.create_or_update(
name: 'my asset test',
calendar_id: calendar1.id,
condition: {
'ticket.state_id' => {
operator: 'is',
value: [ ticket_state1.id, ticket_state2.id ],
},
'ticket.owner_id' => {
operator: 'is',
pre_condition: 'specific',
value: user1.id,
value_completion: 'John Smith <john.smith@example.com>'
},
},
)
assets = sla.assets({})
assert(assets[:User][user1.id], 'check assets')
assert_not(assets[:User][user2.id], 'check assets')
assert(assets[:TicketState][ticket_state1.id], 'check assets')
assert(assets[:TicketState][ticket_state2.id], 'check assets')
assert(assets[:Calendar][calendar1.id], 'check assets')
end
end

View file

@ -1,76 +0,0 @@
require 'test_helper'
class TriggerAssetsTest < ActiveSupport::TestCase
test 'assets' do
UserInfo.current_user_id = 1
roles = Role.where(name: %w[Customer])
user1 = User.create_or_update(
login: 'assets_trigger1@example.org',
firstname: 'assets_trigger1',
lastname: 'assets_trigger1',
email: 'assets_trigger1@example.org',
password: 'some_pass',
active: true,
roles: roles,
)
user2 = User.create_or_update(
login: 'assets_trigger2@example.org',
firstname: 'assets_trigger2',
lastname: 'assets_trigger2',
email: 'assets_trigger2@example.org',
password: 'some_pass',
active: true,
roles: roles,
)
user3 = User.create_or_update(
login: 'assets_trigger3@example.org',
firstname: 'assets_trigger3',
lastname: 'assets_trigger3',
email: 'assets_trigger3@example.org',
password: 'some_pass',
active: true,
roles: roles,
)
group1 = Group.create_or_update(
name: 'group1_trigger',
active: true,
)
ticket_state1 = Ticket::State.find_by(name: 'new')
ticket_state2 = Ticket::State.find_by(name: 'open')
ticket_priority2 = Ticket::Priority.find_by(name: '2 normal')
trigger = Trigger.create_or_update(
name: 'my trigger',
condition: {
'ticket.state_id' => {
operator: 'is',
value: [ ticket_state1.id ],
},
'ticket.owner_id' => {
operator: 'is',
pre_condition: 'specific',
value: user1.id,
value_completion: 'John Smith <john.smith@example.com>'
},
},
perform: {
'ticket.group_id' => {
value: group1.id.to_s,
},
},
disable_notification: true,
)
assets = trigger.assets({})
assert(assets[:User][user1.id])
assert_not(assets[:User][user2.id])
assert_not(assets[:User][user3.id])
assert(assets[:TicketState][ticket_state1.id])
assert_not(assets[:TicketState][ticket_state2.id])
assert_not(assets[:TicketPriority])
assert(assets[:Group][group1.id])
end
end

View file

@ -1,3 +1,15 @@
# NOTE: This test file is _almost_ fully migrated to RSpec, as of 4cc64d0ce.
# It may be deleted once all missing spec coverage has been added.
#
# What's missing is coverage for
# the non-standard implementation of #assets on the User class.
# (It adds an { accounts: {} } key-value pair
# to the resulting User attributes hash;
# see lines 75:83:91:109:123:131:139 of this file).
#
# This omission is discussed in detail in
# https://git.znuny.com/zammad/zammad/merge_requests/363
require 'test_helper'
class UserAssetsTest < ActiveSupport::TestCase