Refactoring: Migrate history_test to RSpec

This commit is contained in:
Ryan Lue 2019-06-28 15:07:14 +02:00 committed by Thorsten Eckel
parent a1da3a27f9
commit 4a1477b1ff
8 changed files with 349 additions and 499 deletions

View file

@ -149,73 +149,54 @@ returns
=end
def self.list(requested_object, requested_object_id, related_history_object = nil, assets = nil)
if related_history_object.blank?
history_object = object_lookup(requested_object)
history = History.where(history_object_id: history_object.id)
.where(o_id: requested_object_id)
.order(created_at: :asc)
else
history_object_requested = object_lookup(requested_object)
history_object_related = object_lookup(related_history_object)
history = History.where(
'((history_object_id = ? AND o_id = ?) OR (history_object_id = ? AND related_o_id = ? ))',
history_object_requested.id,
requested_object_id,
history_object_related.id,
requested_object_id,
histories = History.where(
history_object_id: object_lookup(requested_object).id,
o_id: requested_object_id
)
if related_history_object.present?
histories = histories.or(
History.where(
history_object_id: object_lookup(related_history_object).id,
related_o_id: requested_object_id
)
)
.order(created_at: :asc)
end
asset_list = {}
list = []
history.each do |item|
if assets
asset_list = item.assets(asset_list)
end
histories = histories.order(:created_at)
data = item.attributes
data['object'] = object_lookup_id(data['history_object_id']).name
data['type'] = type_lookup_id(data['history_type_id']).name
data.delete('history_object_id')
data.delete('history_type_id')
list = histories.map(&:attributes).each do |data|
data['object'] = History::Object.lookup(id: data.delete('history_object_id'))&.name
data['type'] = History::Type.lookup(id: data.delete('history_type_id'))&.name
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
data.delete('history_attribute_id')
data.delete('updated_at')
data.delete('related_o_id') if data['related_o_id'].nil?
if data['id_to'].nil? && data['id_from'].nil?
data.delete('id_to')
data.delete('id_from')
data.delete('id_to')
end
if data['value_to'].nil? && data['value_from'].nil?
data.delete('value_to')
data.delete('value_from')
data.delete('value_to')
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
if assets
return {
list: list,
assets: asset_list,
}
end
list
end
def self.type_lookup_id(id)
History::Type.lookup(id: id)
return list if !assets
{
list: list,
assets: histories.reduce({}) { |memo, obj| obj.assets(memo) }
}
end
def self.type_lookup(name)
@ -227,10 +208,6 @@ returns
History::Type.create!(name: name)
end
def self.object_lookup_id(id)
History::Object.lookup(id: id)
end
def self.object_lookup(name)
# lookup
history_object = History::Object.lookup(name: name)
@ -240,10 +217,6 @@ returns
History::Object.create!(name: name)
end
def self.attribute_lookup_id(id)
History::Attribute.lookup(id: id)
end
def self.attribute_lookup(name)
# lookup
history_attribute = History::Attribute.lookup(name: name)
@ -253,13 +226,7 @@ returns
History::Attribute.create!(name: name)
end
class Object < ApplicationModel
end
class Type < ApplicationModel
end
class Attribute < ApplicationModel
end
class Object < ApplicationModel; end
class Type < ApplicationModel; end
class Attribute < ApplicationModel; end
end

View 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

View file

@ -5,4 +5,188 @@ require 'models/concerns/can_be_imported_examples'
RSpec.describe History, type: :model do
it_behaves_like 'ApplicationModel', can_assets: { own_attributes: false }
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 records 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 records FE assets' do
expect(list).to include(
assets: matching_histories.reduce({}) { |assets, h| h.assets(assets) }
)
end
end
end
end
end
end

View file

@ -1,6 +1,7 @@
require 'rails_helper'
require 'models/application_model_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_xss_sanitized_note_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
it_behaves_like 'ApplicationModel', can_assets: { associations: :members }
it_behaves_like 'CanLookup'
it_behaves_like 'HasHistory'
it_behaves_like 'HasSearchIndexBackend', indexed_factory: :organization
it_behaves_like 'HasXssSanitizedNote', model_factory: :organization
it_behaves_like 'HasObjectManagerAttributesValidation'

View file

@ -1,11 +1,13 @@
require 'rails_helper'
require 'models/application_model_examples'
require 'models/concerns/can_be_imported_examples'
require 'models/concerns/has_history_examples'
require 'models/concerns/has_object_manager_attributes_validation_examples'
RSpec.describe Ticket::Article, type: :model do
it_behaves_like 'ApplicationModel'
it_behaves_like 'CanBeImported'
it_behaves_like 'HasHistory'
it_behaves_like 'HasObjectManagerAttributesValidation'
subject(:article) { create(:ticket_article) }

View file

@ -2,6 +2,7 @@ require 'rails_helper'
require 'models/application_model_examples'
require 'models/concerns/can_be_imported_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_object_manager_attributes_validation_examples'
@ -9,6 +10,7 @@ RSpec.describe Ticket, type: :model do
it_behaves_like 'ApplicationModel'
it_behaves_like 'CanBeImported'
it_behaves_like 'CanLookup'
it_behaves_like 'HasHistory', history_relation_object: 'Ticket::Article'
it_behaves_like 'HasXssSanitizedNote', model_factory: :ticket
it_behaves_like 'HasObjectManagerAttributesValidation'

View file

@ -1,6 +1,7 @@
require 'rails_helper'
require 'models/application_model_examples'
require 'models/concerns/has_groups_examples'
require 'models/concerns/has_history_examples'
require 'models/concerns/has_roles_examples'
require 'models/concerns/has_groups_permissions_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 'HasGroups', group_access_factory: :agent_user
it_behaves_like 'HasHistory'
it_behaves_like 'HasRoles', group_access_factory: :agent_user
it_behaves_like 'HasXssSanitizedNote', model_factory: :user
it_behaves_like 'HasGroups and Permissions', group_access_no_permission_factory: :user

View file

@ -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