audited 4.5.0 → 4.10.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of audited might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/.gitignore +0 -1
- data/.rubocop.yml +25 -0
- data/.travis.yml +37 -16
- data/Appraisals +33 -11
- data/CHANGELOG.md +151 -0
- data/README.md +125 -39
- data/gemfiles/rails42.gemfile +4 -1
- data/gemfiles/rails50.gemfile +4 -1
- data/gemfiles/rails51.gemfile +5 -2
- data/gemfiles/rails52.gemfile +10 -0
- data/gemfiles/rails60.gemfile +10 -0
- data/gemfiles/rails61.gemfile +10 -0
- data/lib/audited.rb +4 -2
- data/lib/audited/audit.rb +39 -14
- data/lib/audited/auditor.rb +223 -72
- data/lib/audited/rspec_matchers.rb +70 -21
- data/lib/audited/version.rb +1 -1
- data/lib/generators/audited/templates/add_version_to_auditable_index.rb +21 -0
- data/lib/generators/audited/templates/install.rb +2 -2
- data/lib/generators/audited/templates/revert_polymorphic_indexes_order.rb +20 -0
- data/lib/generators/audited/upgrade_generator.rb +9 -0
- data/spec/audited/audit_spec.rb +93 -4
- data/spec/audited/auditor_spec.rb +473 -57
- data/spec/audited/rspec_matchers_spec.rb +69 -0
- data/spec/audited/sweeper_spec.rb +15 -6
- data/spec/audited_spec_helpers.rb +16 -2
- data/spec/rails_app/app/assets/config/manifest.js +1 -0
- data/spec/rails_app/app/controllers/application_controller.rb +2 -0
- data/spec/rails_app/config/application.rb +5 -0
- data/spec/rails_app/config/database.yml +1 -0
- data/spec/spec_helper.rb +4 -1
- data/spec/support/active_record/models.rb +51 -4
- data/spec/support/active_record/schema.rb +4 -2
- data/test/db/version_6.rb +2 -0
- data/test/test_helper.rb +1 -2
- data/test/upgrade_generator_test.rb +10 -0
- metadata +62 -22
- data/gemfiles/rails40.gemfile +0 -9
- data/gemfiles/rails41.gemfile +0 -8
@@ -41,12 +41,12 @@ module Audited
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def only(*fields)
|
44
|
-
@options[:only] = fields.flatten
|
44
|
+
@options[:only] = fields.flatten.map(&:to_s)
|
45
45
|
self
|
46
46
|
end
|
47
47
|
|
48
48
|
def except(*fields)
|
49
|
-
@options[:except] = fields.flatten
|
49
|
+
@options[:except] = fields.flatten.map(&:to_s)
|
50
50
|
self
|
51
51
|
end
|
52
52
|
|
@@ -56,16 +56,13 @@ module Audited
|
|
56
56
|
end
|
57
57
|
|
58
58
|
def on(*actions)
|
59
|
-
@options[:on] = actions.flatten
|
59
|
+
@options[:on] = actions.flatten.map(&:to_sym)
|
60
60
|
self
|
61
61
|
end
|
62
62
|
|
63
63
|
def matches?(subject)
|
64
64
|
@subject = subject
|
65
|
-
auditing_enabled? &&
|
66
|
-
associated_with_model? &&
|
67
|
-
records_changes_to_specified_fields? &&
|
68
|
-
comment_required_valid?
|
65
|
+
auditing_enabled? && required_checks_for_options_satisfied?
|
69
66
|
end
|
70
67
|
|
71
68
|
def failure_message
|
@@ -109,31 +106,83 @@ module Audited
|
|
109
106
|
end
|
110
107
|
|
111
108
|
def records_changes_to_specified_fields?
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
109
|
+
ignored_fields = build_ignored_fields_from_options
|
110
|
+
|
111
|
+
expects "non audited columns (#{model_class.non_audited_columns.inspect}) to match (#{ignored_fields})"
|
112
|
+
model_class.non_audited_columns.to_set == ignored_fields.to_set
|
113
|
+
end
|
114
|
+
|
115
|
+
def comment_required_valid?
|
116
|
+
expects "to require audit_comment before #{model_class.audited_options[:on]} when comment required"
|
117
|
+
validate_callbacks_include_presence_of_comment? && destroy_callbacks_include_comment_required?
|
118
|
+
end
|
119
119
|
|
120
|
-
|
121
|
-
|
120
|
+
def only_audit_on_designated_callbacks?
|
121
|
+
{
|
122
|
+
create: [:after, :audit_create],
|
123
|
+
update: [:before, :audit_update],
|
124
|
+
destroy: [:before, :audit_destroy]
|
125
|
+
}.map do |(action, kind_callback)|
|
126
|
+
kind, callback = kind_callback
|
127
|
+
callbacks_for(action, kind: kind).include?(callback) if @options[:on].include?(action)
|
128
|
+
end.compact.all?
|
129
|
+
end
|
130
|
+
|
131
|
+
def validate_callbacks_include_presence_of_comment?
|
132
|
+
if @options[:comment_required] && audited_on_create_or_update?
|
133
|
+
callbacks_for(:validate).include?(:presence_of_audit_comment)
|
122
134
|
else
|
123
135
|
true
|
124
136
|
end
|
125
137
|
end
|
126
138
|
|
127
|
-
def
|
128
|
-
|
129
|
-
|
139
|
+
def audited_on_create_or_update?
|
140
|
+
model_class.audited_options[:on].include?(:create) || model_class.audited_options[:on].include?(:update)
|
141
|
+
end
|
130
142
|
|
131
|
-
|
132
|
-
|
143
|
+
def destroy_callbacks_include_comment_required?
|
144
|
+
if @options[:comment_required] && model_class.audited_options[:on].include?(:destroy)
|
145
|
+
callbacks_for(:destroy).include?(:require_comment)
|
133
146
|
else
|
134
147
|
true
|
135
148
|
end
|
136
149
|
end
|
150
|
+
|
151
|
+
def requires_comment_before_callbacks?
|
152
|
+
[:create, :update, :destroy].map do |action|
|
153
|
+
if @options[:comment_required] && model_class.audited_options[:on].include?(action)
|
154
|
+
callbacks_for(action).include?(:require_comment)
|
155
|
+
end
|
156
|
+
end.compact.all?
|
157
|
+
end
|
158
|
+
|
159
|
+
def callbacks_for(action, kind: :before)
|
160
|
+
model_class.send("_#{action}_callbacks").select { |cb| cb.kind == kind }.map(&:filter)
|
161
|
+
end
|
162
|
+
|
163
|
+
def build_ignored_fields_from_options
|
164
|
+
default_ignored_attributes = model_class.default_ignored_attributes
|
165
|
+
|
166
|
+
if @options[:only].present?
|
167
|
+
(default_ignored_attributes | model_class.column_names) - @options[:only]
|
168
|
+
elsif @options[:except].present?
|
169
|
+
default_ignored_attributes | @options[:except]
|
170
|
+
else
|
171
|
+
default_ignored_attributes
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def required_checks_for_options_satisfied?
|
176
|
+
{
|
177
|
+
only: :records_changes_to_specified_fields?,
|
178
|
+
except: :records_changes_to_specified_fields?,
|
179
|
+
comment_required: :comment_required_valid?,
|
180
|
+
associated_with: :associated_with_model?,
|
181
|
+
on: :only_audit_on_designated_callbacks?
|
182
|
+
}.map do |(option, check)|
|
183
|
+
send(check) if @options[option].present?
|
184
|
+
end.compact.all?
|
185
|
+
end
|
137
186
|
end
|
138
187
|
|
139
188
|
class AssociatedAuditMatcher # :nodoc:
|
data/lib/audited/version.rb
CHANGED
@@ -0,0 +1,21 @@
|
|
1
|
+
class <%= migration_class_name %> < <%= migration_parent %>
|
2
|
+
def self.up
|
3
|
+
if index_exists?(:audits, [:auditable_type, :auditable_id], name: index_name)
|
4
|
+
remove_index :audits, name: index_name
|
5
|
+
add_index :audits, [:auditable_type, :auditable_id, :version], name: index_name
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.down
|
10
|
+
if index_exists?(:audits, [:auditable_type, :auditable_id, :version], name: index_name)
|
11
|
+
remove_index :audits, name: index_name
|
12
|
+
add_index :audits, [:auditable_type, :auditable_id], name: index_name
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def index_name
|
19
|
+
'auditable_index'
|
20
|
+
end
|
21
|
+
end
|
@@ -17,8 +17,8 @@ class <%= migration_class_name %> < <%= migration_parent %>
|
|
17
17
|
t.column :created_at, :datetime
|
18
18
|
end
|
19
19
|
|
20
|
-
add_index :audits, [:auditable_id, :
|
21
|
-
add_index :audits, [:
|
20
|
+
add_index :audits, [:auditable_type, :auditable_id, :version], :name => 'auditable_index'
|
21
|
+
add_index :audits, [:associated_type, :associated_id], :name => 'associated_index'
|
22
22
|
add_index :audits, [:user_id, :user_type], :name => 'user_index'
|
23
23
|
add_index :audits, :request_uuid
|
24
24
|
add_index :audits, :created_at
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class <%= migration_class_name %> < <%= migration_parent %>
|
2
|
+
def self.up
|
3
|
+
fix_index_order_for [:associated_id, :associated_type], 'associated_index'
|
4
|
+
fix_index_order_for [:auditable_id, :auditable_type], 'auditable_index'
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.down
|
8
|
+
fix_index_order_for [:associated_type, :associated_id], 'associated_index'
|
9
|
+
fix_index_order_for [:auditable_type, :auditable_id], 'auditable_index'
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def fix_index_order_for(columns, index_name)
|
15
|
+
if index_exists? :audits, columns, name: index_name
|
16
|
+
remove_index :audits, name: index_name
|
17
|
+
add_index :audits, columns.reverse, name: index_name
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -25,6 +25,7 @@ module Audited
|
|
25
25
|
def migrations_to_be_applied
|
26
26
|
Audited::Audit.reset_column_information
|
27
27
|
columns = Audited::Audit.columns.map(&:name)
|
28
|
+
indexes = Audited::Audit.connection.indexes(Audited::Audit.table_name)
|
28
29
|
|
29
30
|
yield :add_comment_to_audits unless columns.include?('comment')
|
30
31
|
|
@@ -53,6 +54,14 @@ module Audited
|
|
53
54
|
if columns.include?('association_id')
|
54
55
|
yield :rename_association_to_associated
|
55
56
|
end
|
57
|
+
|
58
|
+
if indexes.any? { |i| i.columns == %w[associated_id associated_type] }
|
59
|
+
yield :revert_polymorphic_indexes_order
|
60
|
+
end
|
61
|
+
|
62
|
+
if indexes.any? { |i| i.columns == %w[auditable_type auditable_id] }
|
63
|
+
yield :add_version_to_auditable_index
|
64
|
+
end
|
56
65
|
end
|
57
66
|
end
|
58
67
|
end
|
data/spec/audited/audit_spec.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
|
+
SingleCov.covered! uncovered: 1 # Rails version check
|
4
|
+
|
3
5
|
describe Audited::Audit do
|
4
6
|
let(:user) { Models::ActiveRecord::User.new name: "Testing" }
|
5
7
|
|
@@ -51,8 +53,51 @@ describe Audited::Audit do
|
|
51
53
|
end
|
52
54
|
end
|
53
55
|
|
54
|
-
describe "
|
56
|
+
describe "#audited_changes" do
|
57
|
+
let(:audit) { Audited.audit_class.new }
|
58
|
+
|
59
|
+
it "can unserialize yaml from text columns" do
|
60
|
+
audit.audited_changes = {foo: "bar"}
|
61
|
+
expect(audit.audited_changes).to eq foo: "bar"
|
62
|
+
end
|
55
63
|
|
64
|
+
it "does not unserialize from binary columns" do
|
65
|
+
allow(Audited::YAMLIfTextColumnType).to receive(:text_column?).and_return(false)
|
66
|
+
audit.audited_changes = {foo: "bar"}
|
67
|
+
expect(audit.audited_changes).to eq "{:foo=>\"bar\"}"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "#undo" do
|
72
|
+
let(:user) { Models::ActiveRecord::User.create(name: "John") }
|
73
|
+
|
74
|
+
it "undos changes" do
|
75
|
+
user.update_attribute(:name, 'Joe')
|
76
|
+
user.audits.last.undo
|
77
|
+
user.reload
|
78
|
+
expect(user.name).to eq("John")
|
79
|
+
end
|
80
|
+
|
81
|
+
it "undos destroy" do
|
82
|
+
user.destroy
|
83
|
+
user.audits.last.undo
|
84
|
+
user = Models::ActiveRecord::User.find_by(name: "John")
|
85
|
+
expect(user.name).to eq("John")
|
86
|
+
end
|
87
|
+
|
88
|
+
it "undos creation" do
|
89
|
+
user # trigger create
|
90
|
+
expect {user.audits.last.undo}.to change(Models::ActiveRecord::User, :count).by(-1)
|
91
|
+
end
|
92
|
+
|
93
|
+
it "fails when trying to undo unknown" do
|
94
|
+
audit = user.audits.last
|
95
|
+
audit.action = 'oops'
|
96
|
+
expect { audit.undo }.to raise_error("invalid action given oops")
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe "user=" do
|
56
101
|
it "should be able to set the user to a model object" do
|
57
102
|
subject.user = user
|
58
103
|
expect(subject.user).to eq(user)
|
@@ -88,11 +133,9 @@ describe Audited::Audit do
|
|
88
133
|
subject.user = user
|
89
134
|
expect(subject.username).to be_nil
|
90
135
|
end
|
91
|
-
|
92
136
|
end
|
93
137
|
|
94
138
|
describe "revision" do
|
95
|
-
|
96
139
|
it "should recreate attributes" do
|
97
140
|
user = Models::ActiveRecord::User.create name: "1"
|
98
141
|
5.times {|i| user.update_attribute :name, (i + 2).to_s }
|
@@ -126,6 +169,34 @@ describe Audited::Audit do
|
|
126
169
|
end
|
127
170
|
end
|
128
171
|
|
172
|
+
describe ".collection_cache_key" do
|
173
|
+
if ActiveRecord::VERSION::MAJOR >= 5
|
174
|
+
it "uses created at" do
|
175
|
+
Audited::Audit.delete_all
|
176
|
+
audit = Models::ActiveRecord::User.create(name: "John").audits.last
|
177
|
+
audit.update_columns(created_at: Time.zone.parse('2018-01-01'))
|
178
|
+
expect(Audited::Audit.collection_cache_key).to match(/-20180101\d+$/)
|
179
|
+
end
|
180
|
+
else
|
181
|
+
it "is not defined" do
|
182
|
+
expect { Audited::Audit.collection_cache_key }.to raise_error(NoMethodError)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
describe ".assign_revision_attributes" do
|
188
|
+
it "dups when frozen" do
|
189
|
+
user.freeze
|
190
|
+
assigned = Audited::Audit.assign_revision_attributes(user, name: "Bar")
|
191
|
+
expect(assigned.name).to eq "Bar"
|
192
|
+
end
|
193
|
+
|
194
|
+
it "ignores unassignable attributes" do
|
195
|
+
assigned = Audited::Audit.assign_revision_attributes(user, oops: "Bar")
|
196
|
+
expect(assigned.name).to eq "Testing"
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
129
200
|
it "should set the version number on create" do
|
130
201
|
user = Models::ActiveRecord::User.create! name: "Set Version Number"
|
131
202
|
expect(user.audits.first.version).to eq(1)
|
@@ -191,6 +262,25 @@ describe Audited::Audit do
|
|
191
262
|
end
|
192
263
|
end
|
193
264
|
|
265
|
+
it "should support nested as_user" do
|
266
|
+
Audited::Audit.as_user("sidekiq") do
|
267
|
+
company = Models::ActiveRecord::Company.create name: "The auditors"
|
268
|
+
company.name = "The Auditors, Inc"
|
269
|
+
company.save
|
270
|
+
expect(company.audits[-1].user).to eq("sidekiq")
|
271
|
+
|
272
|
+
Audited::Audit.as_user(user) do
|
273
|
+
company.name = "NEW Auditors, Inc"
|
274
|
+
company.save
|
275
|
+
expect(company.audits[-1].user).to eq(user)
|
276
|
+
end
|
277
|
+
|
278
|
+
company.name = "LAST Auditors, Inc"
|
279
|
+
company.save
|
280
|
+
expect(company.audits[-1].user).to eq("sidekiq")
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
194
284
|
it "should record usernames" do
|
195
285
|
Audited::Audit.as_user(user.name) do
|
196
286
|
company = Models::ActiveRecord::Company.create name: "The auditors"
|
@@ -241,6 +331,5 @@ describe Audited::Audit do
|
|
241
331
|
}.to raise_exception('expected')
|
242
332
|
expect(Audited.store[:audited_user]).to be_nil
|
243
333
|
end
|
244
|
-
|
245
334
|
end
|
246
335
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
|
+
SingleCov.covered! uncovered: 13 # not testing proxy_respond_to? hack / 2 methods / deprecation of `version`
|
4
|
+
|
3
5
|
describe Audited::Auditor do
|
4
6
|
|
5
7
|
describe "configuration" do
|
@@ -17,6 +19,132 @@ describe Audited::Auditor do
|
|
17
19
|
end
|
18
20
|
end
|
19
21
|
|
22
|
+
context "should be configurable which conditions are audited" do
|
23
|
+
subject { ConditionalCompany.new.send(:auditing_enabled) }
|
24
|
+
|
25
|
+
context "when condition method is private" do
|
26
|
+
subject { ConditionalPrivateCompany.new.send(:auditing_enabled) }
|
27
|
+
|
28
|
+
before do
|
29
|
+
class ConditionalPrivateCompany < ::ActiveRecord::Base
|
30
|
+
self.table_name = 'companies'
|
31
|
+
|
32
|
+
audited if: :foo?
|
33
|
+
|
34
|
+
private def foo?
|
35
|
+
true
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
it { is_expected.to be_truthy }
|
41
|
+
end
|
42
|
+
|
43
|
+
context "when passing a method name" do
|
44
|
+
before do
|
45
|
+
class ConditionalCompany < ::ActiveRecord::Base
|
46
|
+
self.table_name = 'companies'
|
47
|
+
|
48
|
+
audited if: :public?
|
49
|
+
|
50
|
+
def public?; end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context "when conditions are true" do
|
55
|
+
before { allow_any_instance_of(ConditionalCompany).to receive(:public?).and_return(true) }
|
56
|
+
it { is_expected.to be_truthy }
|
57
|
+
end
|
58
|
+
|
59
|
+
context "when conditions are false" do
|
60
|
+
before { allow_any_instance_of(ConditionalCompany).to receive(:public?).and_return(false) }
|
61
|
+
it { is_expected.to be_falsey }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context "when passing a Proc" do
|
66
|
+
context "when conditions are true" do
|
67
|
+
before do
|
68
|
+
class InclusiveCompany < ::ActiveRecord::Base
|
69
|
+
self.table_name = 'companies'
|
70
|
+
audited if: Proc.new { true }
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
subject { InclusiveCompany.new.send(:auditing_enabled) }
|
75
|
+
|
76
|
+
it { is_expected.to be_truthy }
|
77
|
+
end
|
78
|
+
|
79
|
+
context "when conditions are false" do
|
80
|
+
before do
|
81
|
+
class ExclusiveCompany < ::ActiveRecord::Base
|
82
|
+
self.table_name = 'companies'
|
83
|
+
audited if: Proc.new { false }
|
84
|
+
end
|
85
|
+
end
|
86
|
+
subject { ExclusiveCompany.new.send(:auditing_enabled) }
|
87
|
+
it { is_expected.to be_falsey }
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
context "should be configurable which conditions aren't audited" do
|
93
|
+
context "when using a method name" do
|
94
|
+
before do
|
95
|
+
class ExclusionaryCompany < ::ActiveRecord::Base
|
96
|
+
self.table_name = 'companies'
|
97
|
+
|
98
|
+
audited unless: :non_profit?
|
99
|
+
|
100
|
+
def non_profit?; end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
subject { ExclusionaryCompany.new.send(:auditing_enabled) }
|
105
|
+
|
106
|
+
context "when conditions are true" do
|
107
|
+
before { allow_any_instance_of(ExclusionaryCompany).to receive(:non_profit?).and_return(true) }
|
108
|
+
it { is_expected.to be_falsey }
|
109
|
+
end
|
110
|
+
|
111
|
+
context "when conditions are false" do
|
112
|
+
before { allow_any_instance_of(ExclusionaryCompany).to receive(:non_profit?).and_return(false) }
|
113
|
+
it { is_expected.to be_truthy }
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
context "when using a proc" do
|
118
|
+
context "when conditions are true" do
|
119
|
+
before do
|
120
|
+
class ExclusionaryCompany < ::ActiveRecord::Base
|
121
|
+
self.table_name = 'companies'
|
122
|
+
audited unless: Proc.new { |c| c.exclusive? }
|
123
|
+
|
124
|
+
def exclusive?
|
125
|
+
true
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
subject { ExclusionaryCompany.new.send(:auditing_enabled) }
|
131
|
+
it { is_expected.to be_falsey }
|
132
|
+
end
|
133
|
+
|
134
|
+
context "when conditions are false" do
|
135
|
+
before do
|
136
|
+
class InclusiveCompany < ::ActiveRecord::Base
|
137
|
+
self.table_name = 'companies'
|
138
|
+
audited unless: Proc.new { false }
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
subject { InclusiveCompany.new.send(:auditing_enabled) }
|
143
|
+
it { is_expected.to be_truthy }
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
20
148
|
it "should be configurable which attributes are not audited via ignored_attributes" do
|
21
149
|
Audited.ignored_attributes = ['delta', 'top_secret', 'created_at']
|
22
150
|
class Secret < ::ActiveRecord::Base
|
@@ -36,9 +164,14 @@ describe Audited::Auditor do
|
|
36
164
|
end
|
37
165
|
|
38
166
|
it "should not save non-audited columns" do
|
39
|
-
|
167
|
+
previous = Models::ActiveRecord::User.non_audited_columns
|
168
|
+
begin
|
169
|
+
Models::ActiveRecord::User.non_audited_columns += [:favourite_device]
|
40
170
|
|
41
|
-
|
171
|
+
expect(create_user.audits.first.audited_changes.keys.any? { |col| ['favourite_device', 'created_at', 'updated_at', 'password'].include?( col ) }).to eq(false)
|
172
|
+
ensure
|
173
|
+
Models::ActiveRecord::User.non_audited_columns = previous
|
174
|
+
end
|
42
175
|
end
|
43
176
|
|
44
177
|
it "should not save other columns than specified in 'only' option" do
|
@@ -60,16 +193,68 @@ describe Audited::Auditor do
|
|
60
193
|
expect(user.audits.last.audited_changes.keys).to eq(%w{password})
|
61
194
|
end
|
62
195
|
|
63
|
-
|
196
|
+
it "should save attributes not specified in 'except' option" do
|
197
|
+
user = Models::ActiveRecord::User.create
|
198
|
+
user.instance_eval do
|
199
|
+
def non_column_attr
|
200
|
+
@non_column_attr
|
201
|
+
end
|
202
|
+
|
203
|
+
def non_column_attr=(val)
|
204
|
+
attribute_will_change!("non_column_attr")
|
205
|
+
@non_column_attr = val
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
user.password = "password"
|
210
|
+
user.non_column_attr = "some value"
|
211
|
+
user.save!
|
212
|
+
expect(user.audits.last.audited_changes.keys).to eq(%w{non_column_attr})
|
213
|
+
end
|
214
|
+
|
215
|
+
it "should redact columns specified in 'redacted' option" do
|
216
|
+
redacted = Audited::Auditor::AuditedInstanceMethods::REDACTED
|
217
|
+
user = Models::ActiveRecord::UserRedactedPassword.create(password: "password")
|
218
|
+
user.save!
|
219
|
+
expect(user.audits.last.audited_changes['password']).to eq(redacted)
|
220
|
+
user.password = "new_password"
|
221
|
+
user.save!
|
222
|
+
expect(user.audits.last.audited_changes['password']).to eq([redacted, redacted])
|
223
|
+
end
|
224
|
+
|
225
|
+
it "should redact columns specified in 'redacted' option when there are multiple specified" do
|
226
|
+
redacted = Audited::Auditor::AuditedInstanceMethods::REDACTED
|
227
|
+
user =
|
228
|
+
Models::ActiveRecord::UserMultipleRedactedAttributes.create(
|
229
|
+
password: "password",
|
230
|
+
ssn: 123456789
|
231
|
+
)
|
232
|
+
user.save!
|
233
|
+
expect(user.audits.last.audited_changes['password']).to eq(redacted)
|
234
|
+
expect(user.audits.last.audited_changes['ssn']).to eq(redacted)
|
235
|
+
user.password = "new_password"
|
236
|
+
user.ssn = 987654321
|
237
|
+
user.save!
|
238
|
+
expect(user.audits.last.audited_changes['password']).to eq([redacted, redacted])
|
239
|
+
expect(user.audits.last.audited_changes['ssn']).to eq([redacted, redacted])
|
240
|
+
end
|
241
|
+
|
242
|
+
it "should redact columns in 'redacted' column with custom option" do
|
243
|
+
user = Models::ActiveRecord::UserRedactedPasswordCustomRedaction.create(password: "password")
|
244
|
+
user.save!
|
245
|
+
expect(user.audits.last.audited_changes['password']).to eq(["My", "Custom", "Value", 7])
|
246
|
+
end
|
247
|
+
|
248
|
+
if ActiveRecord::Base.connection.adapter_name == 'PostgreSQL'
|
64
249
|
describe "'json' and 'jsonb' audited_changes column type" do
|
65
250
|
let(:migrations_path) { SPEC_ROOT.join("support/active_record/postgres") }
|
66
251
|
|
67
252
|
after do
|
68
|
-
|
253
|
+
run_migrations(:down, migrations_path)
|
69
254
|
end
|
70
255
|
|
71
256
|
it "should work if column type is 'json'" do
|
72
|
-
|
257
|
+
run_migrations(:up, migrations_path, 1)
|
73
258
|
Audited::Audit.reset_column_information
|
74
259
|
expect(Audited::Audit.columns_hash["audited_changes"].sql_type).to eq("json")
|
75
260
|
|
@@ -80,7 +265,7 @@ describe Audited::Auditor do
|
|
80
265
|
end
|
81
266
|
|
82
267
|
it "should work if column type is 'jsonb'" do
|
83
|
-
|
268
|
+
run_migrations(:up, migrations_path, 2)
|
84
269
|
Audited::Audit.reset_column_information
|
85
270
|
expect(Audited::Audit.columns_hash["audited_changes"].sql_type).to eq("jsonb")
|
86
271
|
|
@@ -114,7 +299,7 @@ describe Audited::Auditor do
|
|
114
299
|
end
|
115
300
|
|
116
301
|
describe "on create" do
|
117
|
-
let( :user ) { create_user audit_comment: "Create" }
|
302
|
+
let( :user ) { create_user status: :reliable, audit_comment: "Create" }
|
118
303
|
|
119
304
|
it "should change the audit count" do
|
120
305
|
expect {
|
@@ -138,6 +323,10 @@ describe Audited::Auditor do
|
|
138
323
|
expect(user.audits.first.audited_changes).to eq(user.audited_attributes)
|
139
324
|
end
|
140
325
|
|
326
|
+
it "should store enum value" do
|
327
|
+
expect(user.audits.first.audited_changes["status"]).to eq(1)
|
328
|
+
end
|
329
|
+
|
141
330
|
it "should store comment" do
|
142
331
|
expect(user.audits.first.comment).to eq('Create')
|
143
332
|
end
|
@@ -156,7 +345,7 @@ describe Audited::Auditor do
|
|
156
345
|
|
157
346
|
describe "on update" do
|
158
347
|
before do
|
159
|
-
@user = create_user( name: 'Brandon', audit_comment: 'Update' )
|
348
|
+
@user = create_user( name: 'Brandon', status: :active, audit_comment: 'Update' )
|
160
349
|
end
|
161
350
|
|
162
351
|
it "should save an audit" do
|
@@ -169,17 +358,22 @@ describe Audited::Auditor do
|
|
169
358
|
end
|
170
359
|
|
171
360
|
it "should set the action to 'update'" do
|
172
|
-
@user.
|
361
|
+
@user.update! name: 'Changed'
|
173
362
|
expect(@user.audits.last.action).to eq('update')
|
174
363
|
expect(Audited::Audit.updates.order(:id).last).to eq(@user.audits.last)
|
175
364
|
expect(@user.audits.updates.last).to eq(@user.audits.last)
|
176
365
|
end
|
177
366
|
|
178
367
|
it "should store the changed attributes" do
|
179
|
-
@user.
|
368
|
+
@user.update! name: 'Changed'
|
180
369
|
expect(@user.audits.last.audited_changes).to eq({ 'name' => ['Brandon', 'Changed'] })
|
181
370
|
end
|
182
371
|
|
372
|
+
it "should store changed enum values" do
|
373
|
+
@user.update! status: 1
|
374
|
+
expect(@user.audits.last.audited_changes["status"]).to eq([0, 1])
|
375
|
+
end
|
376
|
+
|
183
377
|
it "should store audit comment" do
|
184
378
|
expect(@user.audits.last.comment).to eq('Update')
|
185
379
|
end
|
@@ -187,12 +381,12 @@ describe Audited::Auditor do
|
|
187
381
|
it "should not save an audit if only specified on create/destroy" do
|
188
382
|
on_create_destroy = Models::ActiveRecord::OnCreateDestroy.create( name: 'Bart' )
|
189
383
|
expect {
|
190
|
-
on_create_destroy.
|
384
|
+
on_create_destroy.update! name: 'Changed'
|
191
385
|
}.to_not change( Audited::Audit, :count )
|
192
386
|
end
|
193
387
|
|
194
388
|
it "should not save an audit if the value doesn't change after type casting" do
|
195
|
-
@user.
|
389
|
+
@user.update! logins: 0, activated: true
|
196
390
|
expect { @user.update_attribute :logins, '0' }.to_not change( Audited::Audit, :count )
|
197
391
|
expect { @user.update_attribute :activated, 1 }.to_not change( Audited::Audit, :count )
|
198
392
|
expect { @user.update_attribute :activated, '1' }.to_not change( Audited::Audit, :count )
|
@@ -216,7 +410,7 @@ describe Audited::Auditor do
|
|
216
410
|
|
217
411
|
describe "on destroy" do
|
218
412
|
before do
|
219
|
-
@user = create_user
|
413
|
+
@user = create_user(status: :active)
|
220
414
|
end
|
221
415
|
|
222
416
|
it "should save an audit" do
|
@@ -241,6 +435,11 @@ describe Audited::Auditor do
|
|
241
435
|
expect(@user.audits.last.audited_changes).to eq(@user.audited_attributes)
|
242
436
|
end
|
243
437
|
|
438
|
+
it "should store enum value" do
|
439
|
+
@user.destroy
|
440
|
+
expect(@user.audits.last.audited_changes["status"]).to eq(0)
|
441
|
+
end
|
442
|
+
|
244
443
|
it "should be able to reconstruct a destroyed record without history" do
|
245
444
|
@user.audits.delete_all
|
246
445
|
@user.destroy
|
@@ -310,6 +509,77 @@ describe Audited::Auditor do
|
|
310
509
|
end
|
311
510
|
end
|
312
511
|
|
512
|
+
describe "max_audits" do
|
513
|
+
it "should respect global setting" do
|
514
|
+
stub_global_max_audits(10) do
|
515
|
+
expect(Models::ActiveRecord::User.audited_options[:max_audits]).to eq(10)
|
516
|
+
end
|
517
|
+
end
|
518
|
+
|
519
|
+
it "should respect per model setting" do
|
520
|
+
stub_global_max_audits(10) do
|
521
|
+
expect(Models::ActiveRecord::MaxAuditsUser.audited_options[:max_audits]).to eq(5)
|
522
|
+
end
|
523
|
+
end
|
524
|
+
|
525
|
+
it "should delete old audits when keeped amount exceeded" do
|
526
|
+
stub_global_max_audits(2) do
|
527
|
+
user = create_versions(2)
|
528
|
+
user.update(name: 'John')
|
529
|
+
expect(user.audits.pluck(:version)).to eq([2, 3])
|
530
|
+
end
|
531
|
+
end
|
532
|
+
|
533
|
+
it "should not delete old audits when keeped amount not exceeded" do
|
534
|
+
stub_global_max_audits(3) do
|
535
|
+
user = create_versions(2)
|
536
|
+
user.update(name: 'John')
|
537
|
+
expect(user.audits.pluck(:version)).to eq([1, 2, 3])
|
538
|
+
end
|
539
|
+
end
|
540
|
+
|
541
|
+
it "should delete old extra audits after introducing limit" do
|
542
|
+
stub_global_max_audits(nil) do
|
543
|
+
user = Models::ActiveRecord::User.create!(name: 'Brandon', username: 'brandon')
|
544
|
+
user.update!(name: 'Foobar')
|
545
|
+
user.update!(name: 'Awesome', username: 'keepers')
|
546
|
+
user.update!(activated: true)
|
547
|
+
|
548
|
+
Audited.max_audits = 3
|
549
|
+
Models::ActiveRecord::User.send(:normalize_audited_options)
|
550
|
+
user.update!(favourite_device: 'Android Phone')
|
551
|
+
audits = user.audits
|
552
|
+
|
553
|
+
expect(audits.count).to eq(3)
|
554
|
+
expect(audits[0].audited_changes).to include({'name' => ['Foobar', 'Awesome'], 'username' => ['brandon', 'keepers']})
|
555
|
+
expect(audits[1].audited_changes).to eq({'activated' => [nil, true]})
|
556
|
+
expect(audits[2].audited_changes).to eq({'favourite_device' => [nil, 'Android Phone']})
|
557
|
+
end
|
558
|
+
end
|
559
|
+
|
560
|
+
it "should add comment line for combined audit" do
|
561
|
+
stub_global_max_audits(2) do
|
562
|
+
user = Models::ActiveRecord::User.create!(name: 'Foobar 1')
|
563
|
+
user.update(name: 'Foobar 2', audit_comment: 'First audit comment')
|
564
|
+
user.update(name: 'Foobar 3', audit_comment: 'Second audit comment')
|
565
|
+
expect(user.audits.first.comment).to match(/First audit comment.+is the result of multiple/m)
|
566
|
+
end
|
567
|
+
end
|
568
|
+
|
569
|
+
def stub_global_max_audits(max_audits)
|
570
|
+
previous_max_audits = Audited.max_audits
|
571
|
+
previous_user_audited_options = Models::ActiveRecord::User.audited_options.dup
|
572
|
+
begin
|
573
|
+
Audited.max_audits = max_audits
|
574
|
+
Models::ActiveRecord::User.send(:normalize_audited_options) # reloads audited_options
|
575
|
+
yield
|
576
|
+
ensure
|
577
|
+
Audited.max_audits = previous_max_audits
|
578
|
+
Models::ActiveRecord::User.audited_options = previous_user_audited_options
|
579
|
+
end
|
580
|
+
end
|
581
|
+
end
|
582
|
+
|
313
583
|
describe "revisions" do
|
314
584
|
let( :user ) { create_versions }
|
315
585
|
|
@@ -328,8 +598,8 @@ describe Audited::Auditor do
|
|
328
598
|
|
329
599
|
it "should set the attributes for each revision" do
|
330
600
|
u = Models::ActiveRecord::User.create(name: 'Brandon', username: 'brandon')
|
331
|
-
u.
|
332
|
-
u.
|
601
|
+
u.update! name: 'Foobar'
|
602
|
+
u.update! name: 'Awesome', username: 'keepers'
|
333
603
|
|
334
604
|
expect(u.revisions.size).to eql(3)
|
335
605
|
|
@@ -345,8 +615,8 @@ describe Audited::Auditor do
|
|
345
615
|
|
346
616
|
it "access to only recent revisions" do
|
347
617
|
u = Models::ActiveRecord::User.create(name: 'Brandon', username: 'brandon')
|
348
|
-
u.
|
349
|
-
u.
|
618
|
+
u.update! name: 'Foobar'
|
619
|
+
u.update! name: 'Awesome', username: 'keepers'
|
350
620
|
|
351
621
|
expect(u.revisions(2).size).to eq(2)
|
352
622
|
|
@@ -363,7 +633,7 @@ describe Audited::Auditor do
|
|
363
633
|
end
|
364
634
|
|
365
635
|
it "should ignore attributes that have been deleted" do
|
366
|
-
user.audits.last.
|
636
|
+
user.audits.last.update! audited_changes: {old_attribute: 'old value'}
|
367
637
|
expect { user.revisions }.to_not raise_error
|
368
638
|
end
|
369
639
|
end
|
@@ -378,21 +648,21 @@ describe Audited::Auditor do
|
|
378
648
|
it "should find the given revision" do
|
379
649
|
revision = user.revision(3)
|
380
650
|
expect(revision).to be_a_kind_of( Models::ActiveRecord::User )
|
381
|
-
expect(revision.
|
651
|
+
expect(revision.audit_version).to eq(3)
|
382
652
|
expect(revision.name).to eq('Foobar 3')
|
383
653
|
end
|
384
654
|
|
385
655
|
it "should find the previous revision with :previous" do
|
386
656
|
revision = user.revision(:previous)
|
387
|
-
expect(revision.
|
657
|
+
expect(revision.audit_version).to eq(4)
|
388
658
|
#expect(revision).to eq(user.revision(4))
|
389
659
|
expect(revision.attributes).to eq(user.revision(4).attributes)
|
390
660
|
end
|
391
661
|
|
392
662
|
it "should be able to get the previous revision repeatedly" do
|
393
663
|
previous = user.revision(:previous)
|
394
|
-
expect(previous.
|
395
|
-
expect(previous.revision(:previous).
|
664
|
+
expect(previous.audit_version).to eq(4)
|
665
|
+
expect(previous.revision(:previous).audit_version).to eq(3)
|
396
666
|
end
|
397
667
|
|
398
668
|
it "should be able to set protected attributes" do
|
@@ -412,8 +682,8 @@ describe Audited::Auditor do
|
|
412
682
|
|
413
683
|
it "should set the attributes for each revision" do
|
414
684
|
u = Models::ActiveRecord::User.create(name: 'Brandon', username: 'brandon')
|
415
|
-
u.
|
416
|
-
u.
|
685
|
+
u.update! name: 'Foobar'
|
686
|
+
u.update! name: 'Awesome', username: 'keepers'
|
417
687
|
|
418
688
|
expect(u.revision(3).name).to eq('Awesome')
|
419
689
|
expect(u.revision(3).username).to eq('keepers')
|
@@ -425,6 +695,16 @@ describe Audited::Auditor do
|
|
425
695
|
expect(u.revision(1).username).to eq('brandon')
|
426
696
|
end
|
427
697
|
|
698
|
+
it "should correctly restore revision with enum" do
|
699
|
+
u = Models::ActiveRecord::User.create(status: :active)
|
700
|
+
u.update_attribute(:status, :reliable)
|
701
|
+
u.update_attribute(:status, :banned)
|
702
|
+
|
703
|
+
expect(u.revision(3)).to be_banned
|
704
|
+
expect(u.revision(2)).to be_reliable
|
705
|
+
expect(u.revision(1)).to be_active
|
706
|
+
end
|
707
|
+
|
428
708
|
it "should be able to get time for first revision" do
|
429
709
|
suspended_at = Time.zone.now
|
430
710
|
u = Models::ActiveRecord::User.create(suspended_at: suspended_at)
|
@@ -452,6 +732,10 @@ describe Audited::Auditor do
|
|
452
732
|
user.revision(1).save!
|
453
733
|
}.to change( Models::ActiveRecord::User, :count ).by(1)
|
454
734
|
end
|
735
|
+
|
736
|
+
it "should return nil for values greater than the number of revisions" do
|
737
|
+
expect(user.revision(user.revisions.count + 1)).to be_nil
|
738
|
+
end
|
455
739
|
end
|
456
740
|
|
457
741
|
describe "revision_at" do
|
@@ -461,8 +745,8 @@ describe Audited::Auditor do
|
|
461
745
|
audit = user.audits.first
|
462
746
|
audit.created_at = 1.hour.ago
|
463
747
|
audit.save!
|
464
|
-
user.
|
465
|
-
expect(user.revision_at( 2.minutes.ago ).
|
748
|
+
user.update! name: 'updated'
|
749
|
+
expect(user.revision_at( 2.minutes.ago ).audit_version).to eq(1)
|
466
750
|
end
|
467
751
|
|
468
752
|
it "should be nil if given a time before audits" do
|
@@ -470,6 +754,33 @@ describe Audited::Auditor do
|
|
470
754
|
end
|
471
755
|
end
|
472
756
|
|
757
|
+
describe "own_and_associated_audits" do
|
758
|
+
it "should return audits for self and associated audits" do
|
759
|
+
owner = Models::ActiveRecord::Owner.create!
|
760
|
+
company = owner.companies.create!
|
761
|
+
company.update!(name: "Collective Idea")
|
762
|
+
|
763
|
+
other_owner = Models::ActiveRecord::Owner.create!
|
764
|
+
other_owner.companies.create!
|
765
|
+
|
766
|
+
expect(owner.own_and_associated_audits).to match_array(owner.audits + company.audits)
|
767
|
+
end
|
768
|
+
|
769
|
+
it "should order audits by creation time" do
|
770
|
+
owner = Models::ActiveRecord::Owner.create!
|
771
|
+
first_audit = owner.audits.first
|
772
|
+
first_audit.update_column(:created_at, 1.year.ago)
|
773
|
+
|
774
|
+
company = owner.companies.create!
|
775
|
+
second_audit = company.audits.first
|
776
|
+
second_audit.update_column(:created_at, 1.month.ago)
|
777
|
+
|
778
|
+
company.update!(name: "Collective Idea")
|
779
|
+
third_audit = company.audits.last
|
780
|
+
expect(owner.own_and_associated_audits.to_a).to eq([third_audit, second_audit, first_audit])
|
781
|
+
end
|
782
|
+
end
|
783
|
+
|
473
784
|
describe "without auditing" do
|
474
785
|
it "should not save an audit when calling #save_without_auditing" do
|
475
786
|
expect {
|
@@ -490,72 +801,162 @@ describe Audited::Auditor do
|
|
490
801
|
end
|
491
802
|
|
492
803
|
it "should be thread safe using a #without_auditing block" do
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
expect(Models::ActiveRecord::User.auditing_enabled).to eq(true)
|
804
|
+
skip if Models::ActiveRecord::User.connection.class.name.include?("SQLite")
|
805
|
+
|
806
|
+
t1 = Thread.new do
|
807
|
+
expect(Models::ActiveRecord::User.auditing_enabled).to eq(true)
|
808
|
+
Models::ActiveRecord::User.without_auditing do
|
809
|
+
expect(Models::ActiveRecord::User.auditing_enabled).to eq(false)
|
810
|
+
Models::ActiveRecord::User.create!( name: 'Bart' )
|
811
|
+
sleep 1
|
812
|
+
expect(Models::ActiveRecord::User.auditing_enabled).to eq(false)
|
503
813
|
end
|
814
|
+
expect(Models::ActiveRecord::User.auditing_enabled).to eq(true)
|
815
|
+
end
|
816
|
+
|
817
|
+
t2 = Thread.new do
|
818
|
+
sleep 0.5
|
819
|
+
expect(Models::ActiveRecord::User.auditing_enabled).to eq(true)
|
820
|
+
Models::ActiveRecord::User.create!( name: 'Lisa' )
|
821
|
+
end
|
822
|
+
t1.join
|
823
|
+
t2.join
|
824
|
+
|
825
|
+
expect(Models::ActiveRecord::User.find_by_name('Bart').audits.count).to eq(0)
|
826
|
+
expect(Models::ActiveRecord::User.find_by_name('Lisa').audits.count).to eq(1)
|
827
|
+
end
|
828
|
+
|
829
|
+
it "should not save an audit when auditing is globally disabled" do
|
830
|
+
expect(Audited.auditing_enabled).to eq(true)
|
831
|
+
Audited.auditing_enabled = false
|
832
|
+
expect(Models::ActiveRecord::User.auditing_enabled).to eq(false)
|
833
|
+
|
834
|
+
user = create_user
|
835
|
+
expect(user.audits.count).to eq(0)
|
836
|
+
|
837
|
+
Audited.auditing_enabled = true
|
838
|
+
expect(Models::ActiveRecord::User.auditing_enabled).to eq(true)
|
839
|
+
|
840
|
+
user.update!(name: 'Test')
|
841
|
+
expect(user.audits.count).to eq(1)
|
842
|
+
Models::ActiveRecord::User.enable_auditing
|
843
|
+
end
|
844
|
+
end
|
845
|
+
|
846
|
+
describe "with auditing" do
|
847
|
+
it "should save an audit when calling #save_with_auditing" do
|
848
|
+
expect {
|
849
|
+
u = Models::ActiveRecord::User.new(name: 'Brandon')
|
850
|
+
Models::ActiveRecord::User.auditing_enabled = false
|
851
|
+
expect(u.save_with_auditing).to eq(true)
|
852
|
+
Models::ActiveRecord::User.auditing_enabled = true
|
853
|
+
}.to change( Audited::Audit, :count ).by(1)
|
854
|
+
end
|
855
|
+
|
856
|
+
it "should save an audit inside of the #with_auditing block" do
|
857
|
+
expect {
|
858
|
+
Models::ActiveRecord::User.auditing_enabled = false
|
859
|
+
Models::ActiveRecord::User.with_auditing { Models::ActiveRecord::User.create!( name: 'Brandon' ) }
|
860
|
+
Models::ActiveRecord::User.auditing_enabled = true
|
861
|
+
}.to change( Audited::Audit, :count ).by(1)
|
862
|
+
end
|
504
863
|
|
505
|
-
|
506
|
-
|
864
|
+
it "should reset auditing status even it raises an exception" do
|
865
|
+
Models::ActiveRecord::User.disable_auditing
|
866
|
+
Models::ActiveRecord::User.with_auditing { raise } rescue nil
|
867
|
+
expect(Models::ActiveRecord::User.auditing_enabled).to eq(false)
|
868
|
+
Models::ActiveRecord::User.enable_auditing
|
869
|
+
end
|
870
|
+
|
871
|
+
it "should be thread safe using a #with_auditing block" do
|
872
|
+
skip if Models::ActiveRecord::User.connection.class.name.include?("SQLite")
|
873
|
+
|
874
|
+
t1 = Thread.new do
|
875
|
+
Models::ActiveRecord::User.disable_auditing
|
876
|
+
expect(Models::ActiveRecord::User.auditing_enabled).to eq(false)
|
877
|
+
Models::ActiveRecord::User.with_auditing do
|
878
|
+
expect(Models::ActiveRecord::User.auditing_enabled).to eq(true)
|
879
|
+
|
880
|
+
Models::ActiveRecord::User.create!( name: 'Shaggy' )
|
881
|
+
sleep 1
|
507
882
|
expect(Models::ActiveRecord::User.auditing_enabled).to eq(true)
|
508
|
-
Models::ActiveRecord::User.create!( name: 'Lisa' )
|
509
883
|
end
|
510
|
-
|
511
|
-
|
884
|
+
expect(Models::ActiveRecord::User.auditing_enabled).to eq(false)
|
885
|
+
Models::ActiveRecord::User.enable_auditing
|
886
|
+
end
|
512
887
|
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
888
|
+
t2 = Thread.new do
|
889
|
+
sleep 0.5
|
890
|
+
Models::ActiveRecord::User.disable_auditing
|
891
|
+
expect(Models::ActiveRecord::User.auditing_enabled).to eq(false)
|
892
|
+
Models::ActiveRecord::User.create!( name: 'Scooby' )
|
893
|
+
Models::ActiveRecord::User.enable_auditing
|
517
894
|
end
|
895
|
+
t1.join
|
896
|
+
t2.join
|
897
|
+
|
898
|
+
Models::ActiveRecord::User.enable_auditing
|
899
|
+
expect(Models::ActiveRecord::User.find_by_name('Shaggy').audits.count).to eq(1)
|
900
|
+
expect(Models::ActiveRecord::User.find_by_name('Scooby').audits.count).to eq(0)
|
518
901
|
end
|
519
902
|
end
|
520
903
|
|
521
904
|
describe "comment required" do
|
522
905
|
|
523
906
|
describe "on create" do
|
524
|
-
it "should not validate when audit_comment is not supplied" do
|
525
|
-
expect(Models::ActiveRecord::CommentRequiredUser.new).not_to be_valid
|
907
|
+
it "should not validate when audit_comment is not supplied when initialized" do
|
908
|
+
expect(Models::ActiveRecord::CommentRequiredUser.new(name: 'Foo')).not_to be_valid
|
909
|
+
end
|
910
|
+
|
911
|
+
it "should not validate when audit_comment is not supplied trying to create" do
|
912
|
+
expect(Models::ActiveRecord::CommentRequiredUser.create(name: 'Foo')).not_to be_valid
|
526
913
|
end
|
527
914
|
|
528
915
|
it "should validate when audit_comment is supplied" do
|
529
|
-
expect(Models::ActiveRecord::CommentRequiredUser.
|
916
|
+
expect(Models::ActiveRecord::CommentRequiredUser.create(name: 'Foo', audit_comment: 'Create')).to be_valid
|
917
|
+
end
|
918
|
+
|
919
|
+
it "should validate when audit_comment is not supplied, and creating is not being audited" do
|
920
|
+
expect(Models::ActiveRecord::OnUpdateCommentRequiredUser.create(name: 'Foo')).to be_valid
|
921
|
+
expect(Models::ActiveRecord::OnDestroyCommentRequiredUser.create(name: 'Foo')).to be_valid
|
530
922
|
end
|
531
923
|
|
532
924
|
it "should validate when audit_comment is not supplied, and auditing is disabled" do
|
533
925
|
Models::ActiveRecord::CommentRequiredUser.disable_auditing
|
534
|
-
expect(Models::ActiveRecord::CommentRequiredUser.
|
926
|
+
expect(Models::ActiveRecord::CommentRequiredUser.create(name: 'Foo')).to be_valid
|
535
927
|
Models::ActiveRecord::CommentRequiredUser.enable_auditing
|
536
928
|
end
|
537
929
|
end
|
538
930
|
|
539
931
|
describe "on update" do
|
540
932
|
let( :user ) { Models::ActiveRecord::CommentRequiredUser.create!( audit_comment: 'Create' ) }
|
933
|
+
let( :on_create_user ) { Models::ActiveRecord::OnDestroyCommentRequiredUser.create }
|
934
|
+
let( :on_destroy_user ) { Models::ActiveRecord::OnDestroyCommentRequiredUser.create }
|
541
935
|
|
542
936
|
it "should not validate when audit_comment is not supplied" do
|
543
|
-
expect(user.
|
937
|
+
expect(user.update(name: 'Test')).to eq(false)
|
938
|
+
end
|
939
|
+
|
940
|
+
it "should validate when audit_comment is not supplied, and updating is not being audited" do
|
941
|
+
expect(on_create_user.update(name: 'Test')).to eq(true)
|
942
|
+
expect(on_destroy_user.update(name: 'Test')).to eq(true)
|
544
943
|
end
|
545
944
|
|
546
945
|
it "should validate when audit_comment is supplied" do
|
547
|
-
expect(user.
|
946
|
+
expect(user.update(name: 'Test', audit_comment: 'Update')).to eq(true)
|
548
947
|
end
|
549
948
|
|
550
949
|
it "should validate when audit_comment is not supplied, and auditing is disabled" do
|
551
950
|
Models::ActiveRecord::CommentRequiredUser.disable_auditing
|
552
|
-
expect(user.
|
951
|
+
expect(user.update(name: 'Test')).to eq(true)
|
553
952
|
Models::ActiveRecord::CommentRequiredUser.enable_auditing
|
554
953
|
end
|
555
954
|
end
|
556
955
|
|
557
956
|
describe "on destroy" do
|
558
957
|
let( :user ) { Models::ActiveRecord::CommentRequiredUser.create!( audit_comment: 'Create' )}
|
958
|
+
let( :on_create_user ) { Models::ActiveRecord::OnCreateCommentRequiredUser.create!( audit_comment: 'Create' ) }
|
959
|
+
let( :on_update_user ) { Models::ActiveRecord::OnUpdateCommentRequiredUser.create }
|
559
960
|
|
560
961
|
it "should not validate when audit_comment is not supplied" do
|
561
962
|
expect(user.destroy).to eq(false)
|
@@ -566,6 +967,11 @@ describe Audited::Auditor do
|
|
566
967
|
expect(user.destroy).to eq(user)
|
567
968
|
end
|
568
969
|
|
970
|
+
it "should validate when audit_comment is not supplied, and destroying is not being audited" do
|
971
|
+
expect(on_create_user.destroy).to eq(on_create_user)
|
972
|
+
expect(on_update_user.destroy).to eq(on_update_user)
|
973
|
+
end
|
974
|
+
|
569
975
|
it "should validate when audit_comment is not supplied, and auditing is disabled" do
|
570
976
|
Models::ActiveRecord::CommentRequiredUser.disable_auditing
|
571
977
|
expect(user.destroy).to eq(user)
|
@@ -575,6 +981,16 @@ describe Audited::Auditor do
|
|
575
981
|
|
576
982
|
end
|
577
983
|
|
984
|
+
describe "no update with comment only" do
|
985
|
+
let( :user ) { Models::ActiveRecord::NoUpdateWithCommentOnlyUser.create }
|
986
|
+
|
987
|
+
it "does not create an audit when only an audit_comment is present" do
|
988
|
+
user.audit_comment = "Comment"
|
989
|
+
expect { user.save! }.to_not change( Audited::Audit, :count )
|
990
|
+
end
|
991
|
+
|
992
|
+
end
|
993
|
+
|
578
994
|
describe "attr_protected and attr_accessible" do
|
579
995
|
|
580
996
|
it "should not raise error when attr_accessible is set and protected is false" do
|
@@ -596,7 +1012,7 @@ describe Audited::Auditor do
|
|
596
1012
|
it "should record user objects" do
|
597
1013
|
Models::ActiveRecord::Company.audit_as( user ) do
|
598
1014
|
company = Models::ActiveRecord::Company.create name: 'The auditors'
|
599
|
-
company.
|
1015
|
+
company.update! name: 'The Auditors'
|
600
1016
|
|
601
1017
|
company.audits.each do |audit|
|
602
1018
|
expect(audit.user).to eq(user)
|
@@ -607,7 +1023,7 @@ describe Audited::Auditor do
|
|
607
1023
|
it "should record usernames" do
|
608
1024
|
Models::ActiveRecord::Company.audit_as( user.name ) do
|
609
1025
|
company = Models::ActiveRecord::Company.create name: 'The auditors'
|
610
|
-
company.
|
1026
|
+
company.update! name: 'The Auditors'
|
611
1027
|
|
612
1028
|
company.audits.each do |audit|
|
613
1029
|
expect(audit.user).to eq(user.name)
|
@@ -617,7 +1033,7 @@ describe Audited::Auditor do
|
|
617
1033
|
end
|
618
1034
|
|
619
1035
|
describe "after_audit" do
|
620
|
-
let( :user ) {
|
1036
|
+
let( :user ) { Models::ActiveRecord::UserWithAfterAudit.new }
|
621
1037
|
|
622
1038
|
it "should invoke after_audit callback on create" do
|
623
1039
|
expect(user.bogus_attr).to be_nil
|
@@ -627,7 +1043,7 @@ describe Audited::Auditor do
|
|
627
1043
|
end
|
628
1044
|
|
629
1045
|
describe "around_audit" do
|
630
|
-
let( :user ) {
|
1046
|
+
let( :user ) { Models::ActiveRecord::UserWithAfterAudit.new }
|
631
1047
|
|
632
1048
|
it "should invoke around_audit callback on create" do
|
633
1049
|
expect(user.around_attr).to be_nil
|
@@ -642,7 +1058,7 @@ describe Audited::Auditor do
|
|
642
1058
|
expect(company.type).to eq("Models::ActiveRecord::Company::STICompany")
|
643
1059
|
expect {
|
644
1060
|
Models::ActiveRecord::Company.auditing_enabled = false
|
645
|
-
company.
|
1061
|
+
company.update! name: 'STI auditors'
|
646
1062
|
Models::ActiveRecord::Company.auditing_enabled = true
|
647
1063
|
}.to_not change( Audited::Audit, :count )
|
648
1064
|
end
|