Refactoring: Migrate history_test to RSpec
This commit is contained in:
parent
a1da3a27f9
commit
4a1477b1ff
8 changed files with 349 additions and 499 deletions
|
@ -149,73 +149,54 @@ returns
|
||||||
=end
|
=end
|
||||||
|
|
||||||
def self.list(requested_object, requested_object_id, related_history_object = nil, assets = nil)
|
def self.list(requested_object, requested_object_id, related_history_object = nil, assets = nil)
|
||||||
if related_history_object.blank?
|
histories = History.where(
|
||||||
history_object = object_lookup(requested_object)
|
history_object_id: object_lookup(requested_object).id,
|
||||||
history = History.where(history_object_id: history_object.id)
|
o_id: requested_object_id
|
||||||
.where(o_id: requested_object_id)
|
)
|
||||||
.order(created_at: :asc)
|
|
||||||
else
|
if related_history_object.present?
|
||||||
history_object_requested = object_lookup(requested_object)
|
histories = histories.or(
|
||||||
history_object_related = object_lookup(related_history_object)
|
History.where(
|
||||||
history = History.where(
|
history_object_id: object_lookup(related_history_object).id,
|
||||||
'((history_object_id = ? AND o_id = ?) OR (history_object_id = ? AND related_o_id = ? ))',
|
related_o_id: requested_object_id
|
||||||
history_object_requested.id,
|
)
|
||||||
requested_object_id,
|
|
||||||
history_object_related.id,
|
|
||||||
requested_object_id,
|
|
||||||
)
|
)
|
||||||
.order(created_at: :asc)
|
|
||||||
end
|
end
|
||||||
asset_list = {}
|
|
||||||
list = []
|
|
||||||
history.each do |item|
|
|
||||||
|
|
||||||
if assets
|
histories = histories.order(:created_at)
|
||||||
asset_list = item.assets(asset_list)
|
|
||||||
end
|
|
||||||
|
|
||||||
data = item.attributes
|
list = histories.map(&:attributes).each do |data|
|
||||||
data['object'] = object_lookup_id(data['history_object_id']).name
|
data['object'] = History::Object.lookup(id: data.delete('history_object_id'))&.name
|
||||||
data['type'] = type_lookup_id(data['history_type_id']).name
|
data['type'] = History::Type.lookup(id: data.delete('history_type_id'))&.name
|
||||||
data.delete('history_object_id')
|
|
||||||
data.delete('history_type_id')
|
|
||||||
|
|
||||||
if data['history_attribute_id']
|
if data['history_attribute_id']
|
||||||
data['attribute'] = attribute_lookup_id(data['history_attribute_id']).name
|
data['attribute'] = History::Attribute.lookup(id: data.delete('history_attribute_id'))&.name
|
||||||
|
end
|
||||||
|
|
||||||
|
if data['related_history_object_id']
|
||||||
|
data['related_object'] = History::Object.lookup(id: data.delete('related_history_object_id'))&.name
|
||||||
end
|
end
|
||||||
data.delete('history_attribute_id')
|
|
||||||
|
|
||||||
data.delete('updated_at')
|
data.delete('updated_at')
|
||||||
|
data.delete('related_o_id') if data['related_o_id'].nil?
|
||||||
|
|
||||||
if data['id_to'].nil? && data['id_from'].nil?
|
if data['id_to'].nil? && data['id_from'].nil?
|
||||||
data.delete('id_to')
|
|
||||||
data.delete('id_from')
|
data.delete('id_from')
|
||||||
|
data.delete('id_to')
|
||||||
end
|
end
|
||||||
|
|
||||||
if data['value_to'].nil? && data['value_from'].nil?
|
if data['value_to'].nil? && data['value_from'].nil?
|
||||||
data.delete('value_to')
|
|
||||||
data.delete('value_from')
|
data.delete('value_from')
|
||||||
|
data.delete('value_to')
|
||||||
end
|
end
|
||||||
if !data['related_history_object_id'].nil?
|
|
||||||
data['related_object'] = object_lookup_id(data['related_history_object_id']).name
|
|
||||||
end
|
|
||||||
data.delete('related_history_object_id')
|
|
||||||
|
|
||||||
if data['related_o_id'].nil?
|
|
||||||
data.delete('related_o_id')
|
|
||||||
end
|
|
||||||
|
|
||||||
list.push data
|
|
||||||
end
|
end
|
||||||
if assets
|
|
||||||
return {
|
|
||||||
list: list,
|
|
||||||
assets: asset_list,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
list
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.type_lookup_id(id)
|
return list if !assets
|
||||||
History::Type.lookup(id: id)
|
|
||||||
|
{
|
||||||
|
list: list,
|
||||||
|
assets: histories.reduce({}) { |memo, obj| obj.assets(memo) }
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.type_lookup(name)
|
def self.type_lookup(name)
|
||||||
|
@ -227,10 +208,6 @@ returns
|
||||||
History::Type.create!(name: name)
|
History::Type.create!(name: name)
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.object_lookup_id(id)
|
|
||||||
History::Object.lookup(id: id)
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.object_lookup(name)
|
def self.object_lookup(name)
|
||||||
# lookup
|
# lookup
|
||||||
history_object = History::Object.lookup(name: name)
|
history_object = History::Object.lookup(name: name)
|
||||||
|
@ -240,10 +217,6 @@ returns
|
||||||
History::Object.create!(name: name)
|
History::Object.create!(name: name)
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.attribute_lookup_id(id)
|
|
||||||
History::Attribute.lookup(id: id)
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.attribute_lookup(name)
|
def self.attribute_lookup(name)
|
||||||
# lookup
|
# lookup
|
||||||
history_attribute = History::Attribute.lookup(name: name)
|
history_attribute = History::Attribute.lookup(name: name)
|
||||||
|
@ -253,13 +226,7 @@ returns
|
||||||
History::Attribute.create!(name: name)
|
History::Attribute.create!(name: name)
|
||||||
end
|
end
|
||||||
|
|
||||||
class Object < ApplicationModel
|
class Object < ApplicationModel; end
|
||||||
end
|
class Type < ApplicationModel; end
|
||||||
|
class Attribute < ApplicationModel; end
|
||||||
class Type < ApplicationModel
|
|
||||||
end
|
|
||||||
|
|
||||||
class Attribute < ApplicationModel
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
123
spec/models/concerns/has_history_examples.rb
Normal file
123
spec/models/concerns/has_history_examples.rb
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
RSpec.shared_examples 'HasHistory' do |history_relation_object: nil|
|
||||||
|
describe 'auto-creation of history records' do
|
||||||
|
let(:histories) { History.where(history_object_id: History::Object.find_by(name: described_class.name)) }
|
||||||
|
|
||||||
|
context 'on creation' do
|
||||||
|
it 'creates a History record for it' do
|
||||||
|
expect { subject }.to change(histories, :count).by(1)
|
||||||
|
expect(histories.last.history_type.name).to eq('created')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'on update' do
|
||||||
|
let(:histories) do
|
||||||
|
History.where(history_object_id: History::Object.lookup(name: described_class.name).id,
|
||||||
|
history_type_id: History::Type.lookup(name: 'updated').id,
|
||||||
|
history_attribute_id: History::Attribute.find_or_create_by(name: attribute).id)
|
||||||
|
end
|
||||||
|
|
||||||
|
let!(:old_value) { subject.send(attribute) }
|
||||||
|
|
||||||
|
shared_examples 'attribute update' do
|
||||||
|
it 'creates a History record for it' do
|
||||||
|
expect { subject.update(attribute => new_value) }.to change(histories, :count).by(1)
|
||||||
|
expect(histories.last.attributes).to include(attributes)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'of #active' do
|
||||||
|
let(:attribute) { 'active' }
|
||||||
|
let(:new_value) { !subject.active }
|
||||||
|
let(:attributes) { { 'value_from' => old_value.to_s, 'value_to' => new_value.to_s } }
|
||||||
|
|
||||||
|
include_examples 'attribute update' if described_class.attribute_names.include?('active')
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'of #body' do
|
||||||
|
let(:attribute) { 'body' }
|
||||||
|
let(:new_value) { 'Lorem ipsum dolor' }
|
||||||
|
let(:attributes) { { 'value_from' => old_value, 'value_to' => new_value } }
|
||||||
|
|
||||||
|
include_examples 'attribute update' if described_class.attribute_names.include?('body')
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'of #email' do
|
||||||
|
let(:attribute) { 'email' }
|
||||||
|
let(:new_value) { Faker::Internet.email }
|
||||||
|
let(:attributes) { { 'value_from' => old_value, 'value_to' => new_value } }
|
||||||
|
|
||||||
|
include_examples 'attribute update' if described_class.attribute_names.include?('email')
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'of #lastname' do
|
||||||
|
let(:attribute) { 'lastname' }
|
||||||
|
let(:new_value) { 'Foo' }
|
||||||
|
let(:attributes) { { 'value_from' => old_value, 'value_to' => new_value } }
|
||||||
|
|
||||||
|
include_examples 'attribute update' if described_class.attribute_names.include?('lastname')
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'of #name' do
|
||||||
|
let(:attribute) { 'name' }
|
||||||
|
let(:new_value) { 'Foo' }
|
||||||
|
let(:attributes) { { 'value_from' => old_value, 'value_to' => new_value } }
|
||||||
|
|
||||||
|
include_examples 'attribute update' if described_class.attribute_names.include?('name')
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'of #state' do
|
||||||
|
let(:attribute) { 'state' }
|
||||||
|
let(:new_value) { state_class.where.not(id: old_value.id).first }
|
||||||
|
let(:state_class) { "#{described_class.name}::State".constantize }
|
||||||
|
let(:attributes) { { 'value_from' => old_value.name, 'value_to' => new_value.name } }
|
||||||
|
|
||||||
|
include_examples 'attribute update' if described_class.attribute_names.include?('state_id')
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'of #title' do
|
||||||
|
let(:attribute) { 'title' }
|
||||||
|
let(:new_value) { 'foo' }
|
||||||
|
let(:attributes) { { 'value_from' => old_value, 'value_to' => new_value } }
|
||||||
|
|
||||||
|
include_examples 'attribute update' if described_class.attribute_names.include?('title')
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when validations or callbacks prevent update' do
|
||||||
|
shared_examples 'failed attribute update' do
|
||||||
|
it 'does not create a History record for it' do
|
||||||
|
expect { subject.update(attribute => new_value) }.not_to change(histories, :count)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'of #owner' do
|
||||||
|
let(:attribute) { 'owner' }
|
||||||
|
let(:new_value) { create(:customer_user) } # Ticket#owner is restricted to active agents of the same group
|
||||||
|
|
||||||
|
include_examples 'failed attribute update' if described_class.attribute_names.include?('owner_id')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#history_get' do
|
||||||
|
context 'without "full" flag' do
|
||||||
|
it 'delegates to History.list for self' do
|
||||||
|
expect(History).to receive(:list).with(described_class.name, subject.id, history_relation_object)
|
||||||
|
|
||||||
|
subject.history_get
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with "full" flag' do
|
||||||
|
it 'returns a hash including History.list for self' do
|
||||||
|
expect(subject.history_get(true))
|
||||||
|
.to include(history: History.list(described_class.name, subject.id, history_relation_object))
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns a hash including FE assets of self and related objects' do
|
||||||
|
expect(subject.history_get(true))
|
||||||
|
.to include(assets: hash_including(subject.assets({})))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -5,4 +5,188 @@ require 'models/concerns/can_be_imported_examples'
|
||||||
RSpec.describe History, type: :model do
|
RSpec.describe History, type: :model do
|
||||||
it_behaves_like 'ApplicationModel', can_assets: { own_attributes: false }
|
it_behaves_like 'ApplicationModel', can_assets: { own_attributes: false }
|
||||||
it_behaves_like 'CanBeImported'
|
it_behaves_like 'CanBeImported'
|
||||||
|
|
||||||
|
describe '.list' do
|
||||||
|
context 'when given an object with no histories' do
|
||||||
|
let!(:object) { create(:'cti/log') }
|
||||||
|
|
||||||
|
it 'returns an empty array' do
|
||||||
|
expect(History.list(object.class.name, object.id))
|
||||||
|
.to be_an(Array).and be_empty
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when given an object with histories' do
|
||||||
|
context 'and called without "related_history_object" argument' do
|
||||||
|
let!(:object) { create(:user) }
|
||||||
|
|
||||||
|
before { object.update(email: 'foo@example.com') }
|
||||||
|
|
||||||
|
context 'or "assets" flag' do
|
||||||
|
let(:list) { History.list(object.class.name, object.id) }
|
||||||
|
|
||||||
|
it 'returns an array of attribute hashes for those histories' do
|
||||||
|
expect(list).to match_array(
|
||||||
|
[
|
||||||
|
hash_including(
|
||||||
|
'o_id' => object.id,
|
||||||
|
),
|
||||||
|
hash_including(
|
||||||
|
'o_id' => object.id,
|
||||||
|
'value_to' => 'foo@example.com',
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'replaces *_id attributes with the corresponding association #name' do
|
||||||
|
expect(list.first)
|
||||||
|
.to not_include('history_object_id', 'history_type_id')
|
||||||
|
.and include(
|
||||||
|
'object' => object.class.name,
|
||||||
|
'type' => 'created',
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(list.second)
|
||||||
|
.to not_include('history_object_id', 'history_type_id', 'history_attribute_id')
|
||||||
|
.and include(
|
||||||
|
'object' => object.class.name,
|
||||||
|
'type' => 'updated',
|
||||||
|
'attribute' => 'email',
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'but with "assets" flag' do
|
||||||
|
let(:list) { History.list(object.class.name, object.id, nil, true) }
|
||||||
|
let(:matching_histories) do
|
||||||
|
History.where(
|
||||||
|
o_id: object.id,
|
||||||
|
history_object_id: History::Object.lookup(name: object.class.name).id
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns a hash including an array of history attribute hashes' do
|
||||||
|
expect(list).to include(
|
||||||
|
list: [
|
||||||
|
hash_including(
|
||||||
|
'o_id' => object.id,
|
||||||
|
'object' => object.class.name,
|
||||||
|
'type' => 'created',
|
||||||
|
),
|
||||||
|
hash_including(
|
||||||
|
'o_id' => object.id,
|
||||||
|
'object' => object.class.name,
|
||||||
|
'type' => 'updated',
|
||||||
|
'attribute' => 'email',
|
||||||
|
'value_to' => 'foo@example.com',
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns a hash including each history record’s FE assets' do
|
||||||
|
expect(list).to include(
|
||||||
|
assets: matching_histories.reduce({}) { |assets, h| h.assets(assets) }
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with "related_history_object" argument' do
|
||||||
|
let!(:object) { related_object.ticket }
|
||||||
|
let!(:related_object) { create(:ticket_article, internal: true) } # MUST be internal, or else callbacks will create additional histories
|
||||||
|
|
||||||
|
before { object.update(title: 'Lorem ipsum dolor') }
|
||||||
|
|
||||||
|
context 'but no "assets" flag' do
|
||||||
|
let(:list) { History.list(object.class.name, object.id, 'Ticket::Article') }
|
||||||
|
|
||||||
|
it 'returns an array of attribute hashes for those histories' do
|
||||||
|
expect(list).to match_array(
|
||||||
|
[
|
||||||
|
hash_including(
|
||||||
|
'o_id' => object.id,
|
||||||
|
),
|
||||||
|
hash_including(
|
||||||
|
'o_id' => related_object.id,
|
||||||
|
),
|
||||||
|
hash_including(
|
||||||
|
'o_id' => object.id,
|
||||||
|
'value_to' => 'Lorem ipsum dolor',
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'replaces *_id attributes with the corresponding association #name' do
|
||||||
|
expect(list.first)
|
||||||
|
.to not_include('history_object_id', 'history_type_id')
|
||||||
|
.and include(
|
||||||
|
'object' => object.class.name,
|
||||||
|
'type' => 'created',
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(list.second)
|
||||||
|
.to not_include('history_object_id', 'history_type_id')
|
||||||
|
.and include(
|
||||||
|
'object' => related_object.class.name,
|
||||||
|
'type' => 'created',
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(list.third)
|
||||||
|
.to not_include('history_object_id', 'history_type_id', 'history_attribute_id')
|
||||||
|
.and include(
|
||||||
|
'object' => object.class.name,
|
||||||
|
'type' => 'updated',
|
||||||
|
'attribute' => 'title',
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'and "assets" flag' do
|
||||||
|
let(:list) { History.list(object.class.name, object.id, 'Ticket::Article', true) }
|
||||||
|
let(:matching_histories) do
|
||||||
|
History.where(
|
||||||
|
o_id: object.id,
|
||||||
|
history_object_id: History::Object.lookup(name: object.class.name).id
|
||||||
|
) + History.where(
|
||||||
|
o_id: related_object.id,
|
||||||
|
history_object_id: History::Object.lookup(name: related_object.class.name).id
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns a hash including an array of history attribute hashes' do
|
||||||
|
expect(list).to include(
|
||||||
|
list: [
|
||||||
|
hash_including(
|
||||||
|
'o_id' => object.id,
|
||||||
|
'object' => object.class.name,
|
||||||
|
'type' => 'created',
|
||||||
|
),
|
||||||
|
hash_including(
|
||||||
|
'o_id' => related_object.id,
|
||||||
|
'object' => related_object.class.name,
|
||||||
|
'type' => 'created',
|
||||||
|
),
|
||||||
|
hash_including(
|
||||||
|
'o_id' => object.id,
|
||||||
|
'object' => object.class.name,
|
||||||
|
'type' => 'updated',
|
||||||
|
'attribute' => 'title',
|
||||||
|
'value_to' => 'Lorem ipsum dolor',
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns a hash including each history record’s FE assets' do
|
||||||
|
expect(list).to include(
|
||||||
|
assets: matching_histories.reduce({}) { |assets, h| h.assets(assets) }
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
require 'models/application_model_examples'
|
require 'models/application_model_examples'
|
||||||
require 'models/concerns/can_lookup_examples'
|
require 'models/concerns/can_lookup_examples'
|
||||||
|
require 'models/concerns/has_history_examples'
|
||||||
require 'models/concerns/has_search_index_backend_examples'
|
require 'models/concerns/has_search_index_backend_examples'
|
||||||
require 'models/concerns/has_xss_sanitized_note_examples'
|
require 'models/concerns/has_xss_sanitized_note_examples'
|
||||||
require 'models/concerns/has_object_manager_attributes_validation_examples'
|
require 'models/concerns/has_object_manager_attributes_validation_examples'
|
||||||
|
@ -8,6 +9,7 @@ require 'models/concerns/has_object_manager_attributes_validation_examples'
|
||||||
RSpec.describe Organization, type: :model do
|
RSpec.describe Organization, type: :model do
|
||||||
it_behaves_like 'ApplicationModel', can_assets: { associations: :members }
|
it_behaves_like 'ApplicationModel', can_assets: { associations: :members }
|
||||||
it_behaves_like 'CanLookup'
|
it_behaves_like 'CanLookup'
|
||||||
|
it_behaves_like 'HasHistory'
|
||||||
it_behaves_like 'HasSearchIndexBackend', indexed_factory: :organization
|
it_behaves_like 'HasSearchIndexBackend', indexed_factory: :organization
|
||||||
it_behaves_like 'HasXssSanitizedNote', model_factory: :organization
|
it_behaves_like 'HasXssSanitizedNote', model_factory: :organization
|
||||||
it_behaves_like 'HasObjectManagerAttributesValidation'
|
it_behaves_like 'HasObjectManagerAttributesValidation'
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
require 'models/application_model_examples'
|
require 'models/application_model_examples'
|
||||||
require 'models/concerns/can_be_imported_examples'
|
require 'models/concerns/can_be_imported_examples'
|
||||||
|
require 'models/concerns/has_history_examples'
|
||||||
require 'models/concerns/has_object_manager_attributes_validation_examples'
|
require 'models/concerns/has_object_manager_attributes_validation_examples'
|
||||||
|
|
||||||
RSpec.describe Ticket::Article, type: :model do
|
RSpec.describe Ticket::Article, type: :model do
|
||||||
it_behaves_like 'ApplicationModel'
|
it_behaves_like 'ApplicationModel'
|
||||||
it_behaves_like 'CanBeImported'
|
it_behaves_like 'CanBeImported'
|
||||||
|
it_behaves_like 'HasHistory'
|
||||||
it_behaves_like 'HasObjectManagerAttributesValidation'
|
it_behaves_like 'HasObjectManagerAttributesValidation'
|
||||||
|
|
||||||
subject(:article) { create(:ticket_article) }
|
subject(:article) { create(:ticket_article) }
|
||||||
|
|
|
@ -2,6 +2,7 @@ require 'rails_helper'
|
||||||
require 'models/application_model_examples'
|
require 'models/application_model_examples'
|
||||||
require 'models/concerns/can_be_imported_examples'
|
require 'models/concerns/can_be_imported_examples'
|
||||||
require 'models/concerns/can_lookup_examples'
|
require 'models/concerns/can_lookup_examples'
|
||||||
|
require 'models/concerns/has_history_examples'
|
||||||
require 'models/concerns/has_xss_sanitized_note_examples'
|
require 'models/concerns/has_xss_sanitized_note_examples'
|
||||||
require 'models/concerns/has_object_manager_attributes_validation_examples'
|
require 'models/concerns/has_object_manager_attributes_validation_examples'
|
||||||
|
|
||||||
|
@ -9,6 +10,7 @@ RSpec.describe Ticket, type: :model do
|
||||||
it_behaves_like 'ApplicationModel'
|
it_behaves_like 'ApplicationModel'
|
||||||
it_behaves_like 'CanBeImported'
|
it_behaves_like 'CanBeImported'
|
||||||
it_behaves_like 'CanLookup'
|
it_behaves_like 'CanLookup'
|
||||||
|
it_behaves_like 'HasHistory', history_relation_object: 'Ticket::Article'
|
||||||
it_behaves_like 'HasXssSanitizedNote', model_factory: :ticket
|
it_behaves_like 'HasXssSanitizedNote', model_factory: :ticket
|
||||||
it_behaves_like 'HasObjectManagerAttributesValidation'
|
it_behaves_like 'HasObjectManagerAttributesValidation'
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
require 'models/application_model_examples'
|
require 'models/application_model_examples'
|
||||||
require 'models/concerns/has_groups_examples'
|
require 'models/concerns/has_groups_examples'
|
||||||
|
require 'models/concerns/has_history_examples'
|
||||||
require 'models/concerns/has_roles_examples'
|
require 'models/concerns/has_roles_examples'
|
||||||
require 'models/concerns/has_groups_permissions_examples'
|
require 'models/concerns/has_groups_permissions_examples'
|
||||||
require 'models/concerns/has_xss_sanitized_note_examples'
|
require 'models/concerns/has_xss_sanitized_note_examples'
|
||||||
|
@ -17,6 +18,7 @@ RSpec.describe User, type: :model do
|
||||||
|
|
||||||
it_behaves_like 'ApplicationModel', can_assets: { associations: :organization }
|
it_behaves_like 'ApplicationModel', can_assets: { associations: :organization }
|
||||||
it_behaves_like 'HasGroups', group_access_factory: :agent_user
|
it_behaves_like 'HasGroups', group_access_factory: :agent_user
|
||||||
|
it_behaves_like 'HasHistory'
|
||||||
it_behaves_like 'HasRoles', group_access_factory: :agent_user
|
it_behaves_like 'HasRoles', group_access_factory: :agent_user
|
||||||
it_behaves_like 'HasXssSanitizedNote', model_factory: :user
|
it_behaves_like 'HasXssSanitizedNote', model_factory: :user
|
||||||
it_behaves_like 'HasGroups and Permissions', group_access_no_permission_factory: :user
|
it_behaves_like 'HasGroups and Permissions', group_access_no_permission_factory: :user
|
||||||
|
|
|
@ -1,432 +0,0 @@
|
||||||
require 'test_helper'
|
|
||||||
|
|
||||||
class HistoryTest < ActiveSupport::TestCase
|
|
||||||
|
|
||||||
test 'ticket' do
|
|
||||||
current_user = User.lookup(email: 'nicole.braun@zammad.org')
|
|
||||||
tests = [
|
|
||||||
|
|
||||||
# test 1
|
|
||||||
{
|
|
||||||
ticket_create: {
|
|
||||||
ticket: {
|
|
||||||
group_id: Group.lookup(name: 'Users').id,
|
|
||||||
customer_id: current_user.id,
|
|
||||||
owner_id: User.lookup(login: '-').id,
|
|
||||||
title: 'Unit Test 1 (äöüß)!',
|
|
||||||
state_id: Ticket::State.lookup(name: 'new').id,
|
|
||||||
priority_id: Ticket::Priority.lookup(name: '2 normal').id,
|
|
||||||
updated_by_id: current_user.id,
|
|
||||||
created_by_id: current_user.id,
|
|
||||||
},
|
|
||||||
article: {
|
|
||||||
updated_by_id: current_user.id,
|
|
||||||
created_by_id: current_user.id,
|
|
||||||
type_id: Ticket::Article::Type.lookup(name: 'phone').id,
|
|
||||||
sender_id: Ticket::Article::Sender.lookup(name: 'Customer').id,
|
|
||||||
from: 'Unit Test <unittest@example.com>',
|
|
||||||
body: 'Unit Test 123',
|
|
||||||
internal: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ticket_update: {
|
|
||||||
ticket: {
|
|
||||||
title: 'Unit Test 1 (äöüß) - update!',
|
|
||||||
state_id: Ticket::State.lookup(name: 'open').id,
|
|
||||||
priority_id: Ticket::Priority.lookup(name: '1 low').id,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
history_check: [
|
|
||||||
{
|
|
||||||
result: true,
|
|
||||||
history_object: 'Ticket',
|
|
||||||
history_type: 'created',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
result: true,
|
|
||||||
history_object: 'Ticket',
|
|
||||||
history_type: 'updated',
|
|
||||||
history_attribute: 'title',
|
|
||||||
value_from: 'Unit Test 1 (äöüß)!',
|
|
||||||
value_to: 'Unit Test 1 (äöüß) - update!',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
result: true,
|
|
||||||
history_object: 'Ticket',
|
|
||||||
history_type: 'updated',
|
|
||||||
history_attribute: 'state',
|
|
||||||
value_from: 'new',
|
|
||||||
value_to: 'open',
|
|
||||||
id_from: Ticket::State.lookup(name: 'new').id,
|
|
||||||
id_to: Ticket::State.lookup(name: 'open').id,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
result: true,
|
|
||||||
history_object: 'Ticket::Article',
|
|
||||||
history_type: 'created',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
result: false,
|
|
||||||
history_object: 'User',
|
|
||||||
history_type: 'updated',
|
|
||||||
},
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
# test 2
|
|
||||||
{
|
|
||||||
ticket_create: {
|
|
||||||
ticket: {
|
|
||||||
group_id: Group.lookup(name: 'Users').id,
|
|
||||||
customer_id: current_user.id,
|
|
||||||
owner_id: User.lookup(login: '-').id,
|
|
||||||
title: 'Unit Test 2 (äöüß)!',
|
|
||||||
state_id: Ticket::State.lookup(name: 'new').id,
|
|
||||||
priority_id: Ticket::Priority.lookup(name: '2 normal').id,
|
|
||||||
updated_by_id: current_user.id,
|
|
||||||
created_by_id: current_user.id,
|
|
||||||
},
|
|
||||||
article: {
|
|
||||||
created_by_id: current_user.id,
|
|
||||||
updated_by_id: current_user.id,
|
|
||||||
type_id: Ticket::Article::Type.lookup(name: 'phone').id,
|
|
||||||
sender_id: Ticket::Article::Sender.lookup(name: 'Customer').id,
|
|
||||||
from: 'Unit Test <unittest@example.com>',
|
|
||||||
body: 'Unit Test 123',
|
|
||||||
internal: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ticket_update: {
|
|
||||||
ticket: {
|
|
||||||
title: 'Unit Test 2 (äöüß) - update!',
|
|
||||||
state_id: Ticket::State.lookup(name: 'open').id,
|
|
||||||
owner_id: current_user.id,
|
|
||||||
},
|
|
||||||
article: {
|
|
||||||
body: 'Unit Test 123 - 2',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
history_check: [
|
|
||||||
{
|
|
||||||
result: true,
|
|
||||||
history_object: 'Ticket',
|
|
||||||
history_type: 'created',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
result: true,
|
|
||||||
history_object: 'Ticket',
|
|
||||||
history_type: 'updated',
|
|
||||||
history_attribute: 'title',
|
|
||||||
value_from: 'Unit Test 2 (äöüß)!',
|
|
||||||
value_to: 'Unit Test 2 (äöüß) - update!',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
result: false,
|
|
||||||
history_object: 'Ticket',
|
|
||||||
history_type: 'updated',
|
|
||||||
history_attribute: 'owner',
|
|
||||||
value_from: '-',
|
|
||||||
value_to: 'Nicole Braun',
|
|
||||||
id_from: User.lookup(login: '-').id,
|
|
||||||
id_to: current_user.id,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
result: true,
|
|
||||||
history_object: 'Ticket::Article',
|
|
||||||
history_type: 'created',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
result: true,
|
|
||||||
history_object: 'Ticket::Article',
|
|
||||||
history_type: 'updated',
|
|
||||||
history_attribute: 'body',
|
|
||||||
value_from: 'Unit Test 123',
|
|
||||||
value_to: 'Unit Test 123 - 2',
|
|
||||||
},
|
|
||||||
]
|
|
||||||
},
|
|
||||||
]
|
|
||||||
tickets = []
|
|
||||||
tests.each do |test|
|
|
||||||
|
|
||||||
ticket = nil
|
|
||||||
article = nil
|
|
||||||
|
|
||||||
# use transaction
|
|
||||||
ActiveRecord::Base.transaction do
|
|
||||||
ticket = Ticket.create!(test[:ticket_create][:ticket])
|
|
||||||
test[:ticket_create][:article][:ticket_id] = ticket.id
|
|
||||||
article = Ticket::Article.create!(test[:ticket_create][:article])
|
|
||||||
|
|
||||||
assert_equal(ticket.class, Ticket)
|
|
||||||
assert_equal(article.class, Ticket::Article)
|
|
||||||
|
|
||||||
# update ticket
|
|
||||||
if test[:ticket_update][:ticket]
|
|
||||||
ticket.update!(test[:ticket_update][:ticket])
|
|
||||||
end
|
|
||||||
if test[:ticket_update][:article]
|
|
||||||
article.update!(test[:ticket_update][:article])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# execute object transaction
|
|
||||||
Observer::Transaction.commit
|
|
||||||
|
|
||||||
# execute background jobs
|
|
||||||
Scheduler.worker(true)
|
|
||||||
|
|
||||||
# remember ticket
|
|
||||||
tickets.push ticket
|
|
||||||
|
|
||||||
# check history
|
|
||||||
history_check(ticket.history_get, test[:history_check])
|
|
||||||
end
|
|
||||||
|
|
||||||
# delete tickets
|
|
||||||
tickets.each(&:destroy!)
|
|
||||||
end
|
|
||||||
|
|
||||||
test 'user' do
|
|
||||||
current_user = User.lookup(email: 'nicole.braun@zammad.org')
|
|
||||||
name = rand(999_999)
|
|
||||||
tests = [
|
|
||||||
|
|
||||||
# test 1
|
|
||||||
{
|
|
||||||
user_create: {
|
|
||||||
user: {
|
|
||||||
login: "some_login_test-#{name}",
|
|
||||||
firstname: 'Bob',
|
|
||||||
lastname: 'Smith',
|
|
||||||
email: "somebody-#{name}@example.com",
|
|
||||||
active: true,
|
|
||||||
updated_by_id: current_user.id,
|
|
||||||
created_by_id: current_user.id,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
user_update: {
|
|
||||||
user: {
|
|
||||||
firstname: 'Bob',
|
|
||||||
lastname: 'Master',
|
|
||||||
email: "master-#{name}@example.com",
|
|
||||||
active: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
history_check: [
|
|
||||||
{
|
|
||||||
result: true,
|
|
||||||
history_object: 'User',
|
|
||||||
history_type: 'created',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
result: true,
|
|
||||||
history_object: 'User',
|
|
||||||
history_type: 'updated',
|
|
||||||
history_attribute: 'lastname',
|
|
||||||
value_from: 'Smith',
|
|
||||||
value_to: 'Master',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
result: true,
|
|
||||||
history_object: 'User',
|
|
||||||
history_type: 'updated',
|
|
||||||
history_attribute: 'email',
|
|
||||||
value_from: "somebody-#{name}@example.com",
|
|
||||||
value_to: "master-#{name}@example.com",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
result: true,
|
|
||||||
history_object: 'User',
|
|
||||||
history_type: 'updated',
|
|
||||||
history_attribute: 'active',
|
|
||||||
value_from: 'true',
|
|
||||||
value_to: 'false',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
|
|
||||||
]
|
|
||||||
users = []
|
|
||||||
tests.each do |test|
|
|
||||||
|
|
||||||
user = nil
|
|
||||||
|
|
||||||
# user transaction
|
|
||||||
ActiveRecord::Base.transaction do
|
|
||||||
user = User.create!(test[:user_create][:user])
|
|
||||||
assert_equal(user.class, User)
|
|
||||||
|
|
||||||
# update user
|
|
||||||
if test[:user_update][:user]
|
|
||||||
test[:user_update][:user][:active] = false
|
|
||||||
user.update!(test[:user_update][:user])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# remember user
|
|
||||||
users.push user
|
|
||||||
|
|
||||||
# check history
|
|
||||||
history_check(user.history_get, test[:history_check])
|
|
||||||
end
|
|
||||||
|
|
||||||
# delete user
|
|
||||||
users.each(&:destroy!)
|
|
||||||
end
|
|
||||||
|
|
||||||
test 'organization' do
|
|
||||||
current_user = User.lookup(email: 'nicole.braun@zammad.org')
|
|
||||||
tests = [
|
|
||||||
|
|
||||||
# test 1
|
|
||||||
{
|
|
||||||
organization_create: {
|
|
||||||
organization: {
|
|
||||||
name: 'Org äöüß',
|
|
||||||
note: 'some note',
|
|
||||||
updated_by_id: current_user.id,
|
|
||||||
created_by_id: current_user.id,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
organization_update: {
|
|
||||||
organization: {
|
|
||||||
name: 'Org 123',
|
|
||||||
note: 'some note',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
history_check: [
|
|
||||||
{
|
|
||||||
result: true,
|
|
||||||
history_object: 'Organization',
|
|
||||||
history_type: 'created',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
result: true,
|
|
||||||
history_object: 'Organization',
|
|
||||||
history_type: 'updated',
|
|
||||||
history_attribute: 'name',
|
|
||||||
value_from: 'Org äöüß',
|
|
||||||
value_to: 'Org 123',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
]
|
|
||||||
organizations = []
|
|
||||||
tests.each do |test|
|
|
||||||
|
|
||||||
organization = nil
|
|
||||||
|
|
||||||
# user transaction
|
|
||||||
ActiveRecord::Base.transaction do
|
|
||||||
organization = Organization.create!(test[:organization_create][:organization])
|
|
||||||
assert_equal(organization.class, Organization)
|
|
||||||
|
|
||||||
# update organization
|
|
||||||
if test[:organization_update][:organization]
|
|
||||||
organization.update!(test[:organization_update][:organization])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# remember user
|
|
||||||
organizations.push organization
|
|
||||||
|
|
||||||
# check history
|
|
||||||
history_check(organization.history_get, test[:history_check])
|
|
||||||
end
|
|
||||||
|
|
||||||
# delete user
|
|
||||||
organizations.each(&:destroy!)
|
|
||||||
end
|
|
||||||
|
|
||||||
test 'ticket assets' do
|
|
||||||
UserInfo.current_user_id = 1
|
|
||||||
agent1 = User.create!(
|
|
||||||
login: 'agent1@example.com',
|
|
||||||
firstname: 'agent',
|
|
||||||
lastname: '1',
|
|
||||||
email: 'agent1@example.com',
|
|
||||||
password: 'agentpw',
|
|
||||||
active: true,
|
|
||||||
roles: Role.where(name: %w[Agent Admin]),
|
|
||||||
groups: Group.all,
|
|
||||||
)
|
|
||||||
current_user = User.lookup(email: 'nicole.braun@zammad.org')
|
|
||||||
UserInfo.current_user_id = current_user.id
|
|
||||||
|
|
||||||
ticket = Ticket.create!(
|
|
||||||
title: 'test 1',
|
|
||||||
group: Group.first,
|
|
||||||
customer_id: current_user.id,
|
|
||||||
state: Ticket::State.lookup(name: 'new'),
|
|
||||||
priority: Ticket::Priority.lookup(name: '2 normal'),
|
|
||||||
)
|
|
||||||
article = Ticket::Article.create!(
|
|
||||||
ticket_id: ticket.id,
|
|
||||||
from: 'some_customer_com-1@example.com',
|
|
||||||
to: 'some_zammad_com-1@example.com',
|
|
||||||
subject: 'com test 1',
|
|
||||||
message_id: 'some@id_com_1',
|
|
||||||
body: 'some message 123',
|
|
||||||
internal: false,
|
|
||||||
sender: Ticket::Article::Sender.find_by(name: 'Customer'),
|
|
||||||
type: Ticket::Article::Type.find_by(name: 'email'),
|
|
||||||
)
|
|
||||||
|
|
||||||
# verify if user of history record is in assets
|
|
||||||
UserInfo.current_user_id = agent1.id
|
|
||||||
ticket.state = Ticket::State.find_by(name: 'closed')
|
|
||||||
ticket.save!
|
|
||||||
|
|
||||||
# update updated_by (to not include agent1 in assets by ticket)
|
|
||||||
UserInfo.current_user_id = current_user.id
|
|
||||||
ticket.priority = Ticket::Priority.find_by(name: '3 high')
|
|
||||||
ticket.save!
|
|
||||||
|
|
||||||
history = ticket.history_get(true)
|
|
||||||
assert(history[:assets][:User][current_user.id])
|
|
||||||
assert(history[:assets][:User][agent1.id])
|
|
||||||
assert(history[:assets][:Ticket][ticket.id])
|
|
||||||
assert(history[:assets][:TicketArticle][article.id])
|
|
||||||
end
|
|
||||||
|
|
||||||
def history_check(history_list, history_check)
|
|
||||||
history_check.each do |check_item|
|
|
||||||
match = false
|
|
||||||
history_list.each do |history_item|
|
|
||||||
next if match
|
|
||||||
next if history_item['object'] != check_item[:history_object]
|
|
||||||
next if history_item['type'] != check_item[:history_type]
|
|
||||||
|
|
||||||
if check_item[:history_attribute]
|
|
||||||
next if check_item[:history_attribute] != history_item['attribute']
|
|
||||||
end
|
|
||||||
match = true
|
|
||||||
if history_item['type'] == check_item[:history_type]
|
|
||||||
assert(true, "History type #{history_item['type']} found!")
|
|
||||||
end
|
|
||||||
if check_item[:history_attribute]
|
|
||||||
assert_equal(check_item[:history_attribute], history_item['attribute'], "check history attribute #{check_item[:history_attribute]}")
|
|
||||||
end
|
|
||||||
if check_item[:value_from]
|
|
||||||
assert_equal(check_item[:value_from], history_item['value_from'], "check history :value_from #{history_item['value_from']} ok")
|
|
||||||
end
|
|
||||||
if check_item[:value_to]
|
|
||||||
assert_equal(check_item[:value_to], history_item['value_to'], "check history :value_to #{history_item['value_to']} ok")
|
|
||||||
end
|
|
||||||
if check_item[:id_from]
|
|
||||||
assert_equal(check_item[:id_from], history_item['id_from'], "check history :id_from #{history_item['id_from']} ok")
|
|
||||||
end
|
|
||||||
if check_item[:id_to]
|
|
||||||
assert_equal(check_item[:id_to], history_item['id_to'], "check history :id_to #{history_item['id_to']} ok")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if check_item[:result]
|
|
||||||
assert(match, "history check not matched! #{check_item.inspect}")
|
|
||||||
else
|
|
||||||
assert_not(match, "history check matched but should not! #{check_item.inspect}")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
Loading…
Reference in a new issue