velocity_audited 5.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/ci.yml +115 -0
  3. data/.gitignore +17 -0
  4. data/.standard.yml +5 -0
  5. data/.yardopts +3 -0
  6. data/Appraisals +44 -0
  7. data/CHANGELOG.md +419 -0
  8. data/Gemfile +3 -0
  9. data/LICENSE +19 -0
  10. data/README.md +433 -0
  11. data/Rakefile +18 -0
  12. data/gemfiles/rails50.gemfile +10 -0
  13. data/gemfiles/rails51.gemfile +10 -0
  14. data/gemfiles/rails52.gemfile +10 -0
  15. data/gemfiles/rails60.gemfile +10 -0
  16. data/gemfiles/rails61.gemfile +10 -0
  17. data/gemfiles/rails70.gemfile +10 -0
  18. data/lib/audited/audit.rb +198 -0
  19. data/lib/audited/auditor.rb +476 -0
  20. data/lib/audited/railtie.rb +16 -0
  21. data/lib/audited/rspec_matchers.rb +228 -0
  22. data/lib/audited/sweeper.rb +67 -0
  23. data/lib/audited/version.rb +5 -0
  24. data/lib/audited-rspec.rb +6 -0
  25. data/lib/audited.rb +49 -0
  26. data/lib/generators/audited/install_generator.rb +27 -0
  27. data/lib/generators/audited/migration.rb +17 -0
  28. data/lib/generators/audited/migration_helper.rb +11 -0
  29. data/lib/generators/audited/templates/add_association_to_audits.rb +13 -0
  30. data/lib/generators/audited/templates/add_comment_to_audits.rb +11 -0
  31. data/lib/generators/audited/templates/add_remote_address_to_audits.rb +12 -0
  32. data/lib/generators/audited/templates/add_request_uuid_to_audits.rb +12 -0
  33. data/lib/generators/audited/templates/add_version_to_auditable_index.rb +23 -0
  34. data/lib/generators/audited/templates/install.rb +32 -0
  35. data/lib/generators/audited/templates/rename_association_to_associated.rb +25 -0
  36. data/lib/generators/audited/templates/rename_changes_to_audited_changes.rb +11 -0
  37. data/lib/generators/audited/templates/rename_parent_to_association.rb +13 -0
  38. data/lib/generators/audited/templates/revert_polymorphic_indexes_order.rb +22 -0
  39. data/lib/generators/audited/upgrade_generator.rb +70 -0
  40. data/lib/velocity_audited.rb +5 -0
  41. data/spec/audited/audit_spec.rb +357 -0
  42. data/spec/audited/auditor_spec.rb +1097 -0
  43. data/spec/audited/rspec_matchers_spec.rb +69 -0
  44. data/spec/audited/sweeper_spec.rb +133 -0
  45. data/spec/audited_spec.rb +18 -0
  46. data/spec/audited_spec_helpers.rb +32 -0
  47. data/spec/rails_app/app/assets/config/manifest.js +2 -0
  48. data/spec/rails_app/config/application.rb +13 -0
  49. data/spec/rails_app/config/database.yml +26 -0
  50. data/spec/rails_app/config/environment.rb +5 -0
  51. data/spec/rails_app/config/environments/test.rb +47 -0
  52. data/spec/rails_app/config/initializers/backtrace_silencers.rb +7 -0
  53. data/spec/rails_app/config/initializers/inflections.rb +2 -0
  54. data/spec/rails_app/config/initializers/secret_token.rb +3 -0
  55. data/spec/rails_app/config/routes.rb +3 -0
  56. data/spec/spec_helper.rb +24 -0
  57. data/spec/support/active_record/models.rb +151 -0
  58. data/spec/support/active_record/postgres/1_change_audited_changes_type_to_json.rb +11 -0
  59. data/spec/support/active_record/postgres/2_change_audited_changes_type_to_jsonb.rb +11 -0
  60. data/spec/support/active_record/schema.rb +90 -0
  61. data/test/db/version_1.rb +17 -0
  62. data/test/db/version_2.rb +18 -0
  63. data/test/db/version_3.rb +18 -0
  64. data/test/db/version_4.rb +19 -0
  65. data/test/db/version_5.rb +17 -0
  66. data/test/db/version_6.rb +19 -0
  67. data/test/install_generator_test.rb +62 -0
  68. data/test/test_helper.rb +18 -0
  69. data/test/upgrade_generator_test.rb +97 -0
  70. metadata +260 -0
@@ -0,0 +1,357 @@
1
+ require "spec_helper"
2
+
3
+ SingleCov.covered! uncovered: 1 # Rails version check
4
+
5
+ class CustomAudit < Audited::Audit
6
+ def custom_method
7
+ "I'm custom!"
8
+ end
9
+ end
10
+
11
+ class TempModel1 < ::ActiveRecord::Base
12
+ self.table_name = :companies
13
+ end
14
+
15
+ class TempModel2 < ::ActiveRecord::Base
16
+ self.table_name = :companies
17
+ end
18
+
19
+ class Models::ActiveRecord::CustomUser < ::ActiveRecord::Base
20
+ end
21
+
22
+ class Models::ActiveRecord::CustomUserSubclass < Models::ActiveRecord::CustomUser
23
+ audited
24
+ end
25
+
26
+ describe Audited::Audit do
27
+ let(:user) { Models::ActiveRecord::User.new name: "Testing" }
28
+
29
+ describe "audit class" do
30
+ around(:example) do |example|
31
+ original_audit_class = Audited.audit_class
32
+
33
+ example.run
34
+
35
+ Audited.config { |config| config.audit_class = original_audit_class }
36
+ end
37
+
38
+ context "when a custom audit class is configured" do
39
+ it "should be used in place of #{described_class}" do
40
+ Audited.config { |config| config.audit_class = CustomAudit }
41
+ TempModel1.audited
42
+
43
+ record = TempModel1.create
44
+
45
+ audit = record.audits.first
46
+ expect(audit).to be_a CustomAudit
47
+ expect(audit.custom_method).to eq "I'm custom!"
48
+ end
49
+ end
50
+
51
+ context "when a custom audit class is not configured" do
52
+ it "should default to #{described_class}" do
53
+ TempModel2.audited
54
+
55
+ record = TempModel2.create
56
+
57
+ audit = record.audits.first
58
+ expect(audit).to be_a Audited::Audit
59
+ expect(audit.respond_to?(:custom_method)).to be false
60
+ end
61
+ end
62
+ end
63
+
64
+ describe "#audited_changes" do
65
+ let(:audit) { Audited.audit_class.new }
66
+
67
+ it "can unserialize yaml from text columns" do
68
+ audit.audited_changes = {foo: "bar"}
69
+ expect(audit.audited_changes).to eq foo: "bar"
70
+ end
71
+
72
+ it "does not unserialize from binary columns" do
73
+ allow(Audited::YAMLIfTextColumnType).to receive(:text_column?).and_return(false)
74
+ audit.audited_changes = {foo: "bar"}
75
+ expect(audit.audited_changes).to eq "{:foo=>\"bar\"}"
76
+ end
77
+ end
78
+
79
+ describe "#undo" do
80
+ let(:user) { Models::ActiveRecord::User.create(name: "John") }
81
+
82
+ it "undos changes" do
83
+ user.update_attribute(:name, "Joe")
84
+ user.audits.last.undo
85
+ user.reload
86
+ expect(user.name).to eq("John")
87
+ end
88
+
89
+ it "undos destroy" do
90
+ user.destroy
91
+ user.audits.last.undo
92
+ user = Models::ActiveRecord::User.find_by(name: "John")
93
+ expect(user.name).to eq("John")
94
+ end
95
+
96
+ it "undos creation" do
97
+ user # trigger create
98
+ expect { user.audits.last.undo }.to change(Models::ActiveRecord::User, :count).by(-1)
99
+ end
100
+
101
+ it "fails when trying to undo unknown" do
102
+ audit = user.audits.last
103
+ audit.action = "oops"
104
+ expect { audit.undo }.to raise_error("invalid action given oops")
105
+ end
106
+ end
107
+
108
+ describe "user=" do
109
+ it "should be able to set the user to a model object" do
110
+ subject.user = user
111
+ expect(subject.user).to eq(user)
112
+ end
113
+
114
+ it "should be able to set the user to nil" do
115
+ subject.user_id = 1
116
+ subject.user_type = "Models::ActiveRecord::User"
117
+ subject.username = "joe"
118
+
119
+ subject.user = nil
120
+
121
+ expect(subject.user).to be_nil
122
+ expect(subject.user_id).to be_nil
123
+ expect(subject.user_type).to be_nil
124
+ expect(subject.username).to be_nil
125
+ end
126
+
127
+ it "should be able to set the user to a string" do
128
+ subject.user = "test"
129
+ expect(subject.user).to eq("test")
130
+ end
131
+
132
+ it "should clear model when setting to a string" do
133
+ subject.user = user
134
+ subject.user = "testing"
135
+ expect(subject.user_id).to be_nil
136
+ expect(subject.user_type).to be_nil
137
+ end
138
+
139
+ it "should clear the username when setting to a model" do
140
+ subject.username = "test"
141
+ subject.user = user
142
+ expect(subject.username).to be_nil
143
+ end
144
+ end
145
+
146
+ describe "revision" do
147
+ it "should recreate attributes" do
148
+ user = Models::ActiveRecord::User.create name: "1"
149
+ 5.times { |i| user.update_attribute :name, (i + 2).to_s }
150
+
151
+ user.audits.each do |audit|
152
+ expect(audit.revision.name).to eq(audit.version.to_s)
153
+ end
154
+ end
155
+
156
+ it "should set protected attributes" do
157
+ u = Models::ActiveRecord::User.create(name: "Brandon")
158
+ u.update_attribute :logins, 1
159
+ u.update_attribute :logins, 2
160
+
161
+ expect(u.audits[2].revision.logins).to eq(2)
162
+ expect(u.audits[1].revision.logins).to eq(1)
163
+ expect(u.audits[0].revision.logins).to eq(0)
164
+ end
165
+
166
+ it "should bypass attribute assignment wrappers" do
167
+ u = Models::ActiveRecord::User.create(name: "<Joe>")
168
+ expect(u.audits.first.revision.name).to eq("&lt;Joe&gt;")
169
+ end
170
+
171
+ it "should work for deleted records" do
172
+ user = Models::ActiveRecord::User.create name: "1"
173
+ user.destroy
174
+ revision = user.audits.last.revision
175
+ expect(revision.name).to eq(user.name)
176
+ expect(revision).to be_a_new_record
177
+ end
178
+ end
179
+
180
+ describe ".collection_cache_key" do
181
+ if ActiveRecord::VERSION::MAJOR >= 5
182
+ it "uses created at" do
183
+ Audited::Audit.delete_all
184
+ audit = Models::ActiveRecord::User.create(name: "John").audits.last
185
+ audit.update_columns(created_at: Time.zone.parse("2018-01-01"))
186
+ expect(Audited::Audit.collection_cache_key).to match(/-20180101\d+$/)
187
+ end
188
+ else
189
+ it "is not defined" do
190
+ expect { Audited::Audit.collection_cache_key }.to raise_error(NoMethodError)
191
+ end
192
+ end
193
+ end
194
+
195
+ describe ".assign_revision_attributes" do
196
+ it "dups when frozen" do
197
+ user.freeze
198
+ assigned = Audited::Audit.assign_revision_attributes(user, name: "Bar")
199
+ expect(assigned.name).to eq "Bar"
200
+ end
201
+
202
+ it "ignores unassignable attributes" do
203
+ assigned = Audited::Audit.assign_revision_attributes(user, oops: "Bar")
204
+ expect(assigned.name).to eq "Testing"
205
+ end
206
+ end
207
+
208
+ it "should set the version number on create" do
209
+ user = Models::ActiveRecord::User.create! name: "Set Version Number"
210
+ expect(user.audits.first.version).to eq(1)
211
+ user.update_attribute :name, "Set to 2"
212
+ expect(user.audits.reload.first.version).to eq(1)
213
+ expect(user.audits.reload.last.version).to eq(2)
214
+ user.destroy
215
+ expect(Audited::Audit.where(auditable_type: "Models::ActiveRecord::User", auditable_id: user.id).last.version).to eq(3)
216
+ end
217
+
218
+ it "should set the request uuid on create" do
219
+ user = Models::ActiveRecord::User.create! name: "Set Request UUID"
220
+ expect(user.audits.reload.first.request_uuid).not_to be_blank
221
+ end
222
+
223
+ describe "reconstruct_attributes" do
224
+ it "should work with the old way of storing just the new value" do
225
+ audits = Audited::Audit.reconstruct_attributes([Audited::Audit.new(audited_changes: {"attribute" => "value"})])
226
+ expect(audits["attribute"]).to eq("value")
227
+ end
228
+ end
229
+
230
+ describe "audited_classes" do
231
+ it "should include audited classes" do
232
+ expect(Audited::Audit.audited_classes).to include(Models::ActiveRecord::User)
233
+ end
234
+
235
+ it "should include subclasses" do
236
+ expect(Audited::Audit.audited_classes).to include(Models::ActiveRecord::CustomUserSubclass)
237
+ end
238
+ end
239
+
240
+ describe "new_attributes" do
241
+ it "should return the audited_changes without modification for create" do
242
+ new_attributes = Audited::Audit.new(audited_changes: {int: 1, array: [1]}, action: :create).new_attributes
243
+ expect(new_attributes).to eq({"int" => 1, "array" => [1]})
244
+ end
245
+
246
+ it "should return a hash that contains the after values of each attribute" do
247
+ new_attributes = Audited::Audit.new(audited_changes: {a: [1, 2], b: [3, 4]}, action: :update).new_attributes
248
+ expect(new_attributes).to eq({"a" => 2, "b" => 4})
249
+ end
250
+
251
+ it "should return the audited_changes without modification for destroy" do
252
+ new_attributes = Audited::Audit.new(audited_changes: {int: 1, array: [1]}, action: :destroy).new_attributes
253
+ expect(new_attributes).to eq({"int" => 1, "array" => [1]})
254
+ end
255
+ end
256
+
257
+ describe "old_attributes" do
258
+ it "should return the audited_changes without modification for create" do
259
+ old_attributes = Audited::Audit.new(audited_changes: {int: 1, array: [1]}, action: :create).new_attributes
260
+ expect(old_attributes).to eq({"int" => 1, "array" => [1]})
261
+ end
262
+
263
+ it "should return a hash that contains the before values of each attribute" do
264
+ old_attributes = Audited::Audit.new(audited_changes: {a: [1, 2], b: [3, 4]}, action: :update).old_attributes
265
+ expect(old_attributes).to eq({"a" => 1, "b" => 3})
266
+ end
267
+
268
+ it "should return the audited_changes without modification for destroy" do
269
+ old_attributes = Audited::Audit.new(audited_changes: {int: 1, array: [1]}, action: :destroy).old_attributes
270
+ expect(old_attributes).to eq({"int" => 1, "array" => [1]})
271
+ end
272
+ end
273
+
274
+ describe "as_user" do
275
+ it "should record user objects" do
276
+ Audited::Audit.as_user(user) do
277
+ company = Models::ActiveRecord::Company.create name: "The auditors"
278
+ company.name = "The Auditors, Inc"
279
+ company.save
280
+
281
+ company.audits.each do |audit|
282
+ expect(audit.user).to eq(user)
283
+ end
284
+ end
285
+ end
286
+
287
+ it "should support nested as_user" do
288
+ Audited::Audit.as_user("sidekiq") do
289
+ company = Models::ActiveRecord::Company.create name: "The auditors"
290
+ company.name = "The Auditors, Inc"
291
+ company.save
292
+ expect(company.audits[-1].user).to eq("sidekiq")
293
+
294
+ Audited::Audit.as_user(user) do
295
+ company.name = "NEW Auditors, Inc"
296
+ company.save
297
+ expect(company.audits[-1].user).to eq(user)
298
+ end
299
+
300
+ company.name = "LAST Auditors, Inc"
301
+ company.save
302
+ expect(company.audits[-1].user).to eq("sidekiq")
303
+ end
304
+ end
305
+
306
+ it "should record usernames" do
307
+ Audited::Audit.as_user(user.name) do
308
+ company = Models::ActiveRecord::Company.create name: "The auditors"
309
+ company.name = "The Auditors, Inc"
310
+ company.save
311
+
312
+ company.audits.each do |audit|
313
+ expect(audit.username).to eq(user.name)
314
+ end
315
+ end
316
+ end
317
+
318
+ if ActiveRecord::Base.connection.adapter_name != "SQLite"
319
+ it "should be thread safe" do
320
+ expect(user.save).to eq(true)
321
+
322
+ t1 = Thread.new do
323
+ Audited::Audit.as_user(user) do
324
+ sleep 1
325
+ expect(Models::ActiveRecord::Company.create(name: "The Auditors, Inc").audits.first.user).to eq(user)
326
+ end
327
+ end
328
+
329
+ t2 = Thread.new do
330
+ Audited::Audit.as_user(user.name) do
331
+ expect(Models::ActiveRecord::Company.create(name: "The Competing Auditors, LLC").audits.first.username).to eq(user.name)
332
+ sleep 0.5
333
+ end
334
+ end
335
+
336
+ t1.join
337
+ t2.join
338
+ end
339
+ end
340
+
341
+ it "should return the value from the yield block" do
342
+ result = Audited::Audit.as_user("foo") do
343
+ 42
344
+ end
345
+ expect(result).to eq(42)
346
+ end
347
+
348
+ it "should reset audited_user when the yield block raises an exception" do
349
+ expect {
350
+ Audited::Audit.as_user("foo") do
351
+ raise StandardError.new("expected")
352
+ end
353
+ }.to raise_exception("expected")
354
+ expect(Audited.store[:audited_user]).to be_nil
355
+ end
356
+ end
357
+ end