journaled 4.2.0 → 5.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +148 -46
  3. data/app/jobs/journaled/delivery_job.rb +17 -28
  4. data/app/models/concerns/journaled/changes.rb +1 -1
  5. data/app/models/journaled/audit_log/event.rb +87 -0
  6. data/app/models/journaled/event.rb +1 -1
  7. data/app/models/journaled/writer.rb +31 -15
  8. data/journaled_schemas/journaled/audit_log/event.json +31 -0
  9. data/lib/journaled/audit_log.rb +194 -0
  10. data/lib/journaled/connection.rb +48 -0
  11. data/lib/journaled/engine.rb +5 -0
  12. data/lib/journaled/errors.rb +3 -0
  13. data/lib/journaled/rspec.rb +86 -0
  14. data/lib/journaled/transaction_ext.rb +31 -0
  15. data/lib/journaled/version.rb +1 -1
  16. data/lib/journaled.rb +17 -11
  17. metadata +43 -84
  18. data/spec/dummy/README.rdoc +0 -28
  19. data/spec/dummy/Rakefile +0 -6
  20. data/spec/dummy/bin/bundle +0 -3
  21. data/spec/dummy/bin/rails +0 -4
  22. data/spec/dummy/bin/rake +0 -4
  23. data/spec/dummy/config/application.rb +0 -25
  24. data/spec/dummy/config/boot.rb +0 -5
  25. data/spec/dummy/config/database.yml +0 -6
  26. data/spec/dummy/config/environment.rb +0 -5
  27. data/spec/dummy/config/environments/development.rb +0 -24
  28. data/spec/dummy/config/environments/test.rb +0 -37
  29. data/spec/dummy/config/initializers/backtrace_silencers.rb +0 -7
  30. data/spec/dummy/config/initializers/cookies_serializer.rb +0 -3
  31. data/spec/dummy/config/initializers/filter_parameter_logging.rb +0 -4
  32. data/spec/dummy/config/initializers/inflections.rb +0 -16
  33. data/spec/dummy/config/initializers/mime_types.rb +0 -4
  34. data/spec/dummy/config/initializers/session_store.rb +0 -3
  35. data/spec/dummy/config/initializers/wrap_parameters.rb +0 -14
  36. data/spec/dummy/config/locales/en.yml +0 -23
  37. data/spec/dummy/config/routes.rb +0 -56
  38. data/spec/dummy/config/secrets.yml +0 -22
  39. data/spec/dummy/config.ru +0 -4
  40. data/spec/dummy/db/schema.rb +0 -18
  41. data/spec/dummy/public/404.html +0 -67
  42. data/spec/dummy/public/422.html +0 -67
  43. data/spec/dummy/public/500.html +0 -66
  44. data/spec/dummy/public/favicon.ico +0 -0
  45. data/spec/jobs/journaled/delivery_job_spec.rb +0 -276
  46. data/spec/lib/journaled_spec.rb +0 -89
  47. data/spec/models/concerns/journaled/actor_spec.rb +0 -47
  48. data/spec/models/concerns/journaled/changes_spec.rb +0 -106
  49. data/spec/models/database_change_protection_spec.rb +0 -109
  50. data/spec/models/journaled/actor_uri_provider_spec.rb +0 -42
  51. data/spec/models/journaled/change_writer_spec.rb +0 -281
  52. data/spec/models/journaled/event_spec.rb +0 -236
  53. data/spec/models/journaled/json_schema_model/validator_spec.rb +0 -133
  54. data/spec/models/journaled/writer_spec.rb +0 -174
  55. data/spec/rails_helper.rb +0 -19
  56. data/spec/spec_helper.rb +0 -24
  57. data/spec/support/environment_spec_helper.rb +0 -16
@@ -1,89 +0,0 @@
1
- require 'rails_helper'
2
-
3
- RSpec.describe Journaled do
4
- it "is enabled in production" do
5
- allow(Rails).to receive(:env).and_return("production")
6
- expect(described_class).to be_enabled
7
- end
8
-
9
- it "is disabled in development" do
10
- allow(Rails).to receive(:env).and_return("development")
11
- expect(described_class).not_to be_enabled
12
- end
13
-
14
- it "is disabled in test" do
15
- allow(Rails).to receive(:env).and_return("test")
16
- expect(described_class).not_to be_enabled
17
- end
18
-
19
- it "is enabled in whatevs" do
20
- allow(Rails).to receive(:env).and_return("whatevs")
21
- expect(described_class).to be_enabled
22
- end
23
-
24
- it "is enabled when explicitly enabled in development" do
25
- with_env(JOURNALED_ENABLED: true) do
26
- allow(Rails).to receive(:env).and_return("development")
27
- expect(described_class).to be_enabled
28
- end
29
- end
30
-
31
- it "is disabled when explicitly disabled in production" do
32
- with_env(JOURNALED_ENABLED: false) do
33
- allow(Rails).to receive(:env).and_return("production")
34
- expect(described_class).not_to be_enabled
35
- end
36
- end
37
-
38
- it "is disabled when explicitly disabled with empty string" do
39
- with_env(JOURNALED_ENABLED: '') do
40
- allow(Rails).to receive(:env).and_return("production")
41
- expect(described_class).not_to be_enabled
42
- end
43
- end
44
-
45
- describe "#actor_uri" do
46
- it "delegates to ActorUriProvider" do
47
- allow(Journaled::ActorUriProvider).to receive(:instance)
48
- .and_return(instance_double(Journaled::ActorUriProvider, actor_uri: "my actor uri"))
49
- expect(described_class.actor_uri).to eq "my actor uri"
50
- end
51
- end
52
-
53
- describe '.detect_queue_adapter!' do
54
- it 'raises an error unless the queue adapter is DB-backed' do
55
- expect { described_class.detect_queue_adapter! }.to raise_error <<~MSG
56
- Journaled has detected an unsupported ActiveJob queue adapter: `:test`
57
-
58
- Journaled jobs must be enqueued transactionally to your primary database.
59
-
60
- Please install the appropriate gems and set `queue_adapter` to one of the following:
61
- - `:delayed`
62
- - `:delayed_job`
63
- - `:good_job`
64
- - `:que`
65
-
66
- Read more at https://github.com/Betterment/journaled
67
- MSG
68
- end
69
-
70
- context 'when the queue adapter is supported' do
71
- before do
72
- stub_const("ActiveJob::QueueAdapters::DelayedAdapter", Class.new)
73
- ActiveJob::Base.disable_test_adapter
74
- ActiveJob::Base.queue_adapter = :delayed
75
- end
76
-
77
- around do |example|
78
- example.run
79
- ensure
80
- ActiveJob::Base.queue_adapter = :test
81
- ActiveJob::Base.enable_test_adapter(ActiveJob::QueueAdapters::TestAdapter.new)
82
- end
83
-
84
- it 'does not raise an error' do
85
- expect { described_class.detect_queue_adapter! }.not_to raise_error
86
- end
87
- end
88
- end
89
- end
@@ -1,47 +0,0 @@
1
- require 'rails_helper'
2
-
3
- # This is a controller mixin, but testing as a model spec!
4
- RSpec.describe Journaled::Actor do
5
- let(:user) { double("User") }
6
- let(:klass) do
7
- Class.new do
8
- cattr_accessor(:before_actions) { [] }
9
-
10
- def self.before_action(method_name, _opts)
11
- before_actions << method_name
12
- end
13
-
14
- include Journaled::Actor
15
-
16
- self.journaled_actor = :current_user
17
-
18
- def current_user
19
- nil
20
- end
21
-
22
- def trigger_before_actions
23
- before_actions.each { |method_name| send(method_name) }
24
- end
25
- end
26
- end
27
-
28
- subject { klass.new }
29
-
30
- it "Stores a thunk returning nil if current_user returns nil" do
31
- subject.trigger_before_actions
32
-
33
- allow(subject).to receive(:current_user).and_return(nil)
34
-
35
- expect(Journaled::Current.journaled_actor_proc.call).to be_nil
36
- expect(Journaled::Current.actor).to be_nil
37
- end
38
-
39
- it "Stores a thunk returning current_user if it is set when called" do
40
- subject.trigger_before_actions
41
-
42
- allow(subject).to receive(:current_user).and_return(user)
43
-
44
- expect(Journaled::Current.journaled_actor_proc.call).to eq user
45
- expect(Journaled::Current.actor).to eq user
46
- end
47
- end
@@ -1,106 +0,0 @@
1
- require 'rails_helper'
2
-
3
- RSpec.describe Journaled::Changes do
4
- let(:klass) do
5
- Class.new do
6
- cattr_accessor :after_create_hooks
7
- self.after_create_hooks = []
8
- cattr_accessor :after_save_hooks
9
- self.after_save_hooks = []
10
- cattr_accessor :after_destroy_hooks
11
- self.after_destroy_hooks = []
12
-
13
- def self.after_create(&hook)
14
- after_create_hooks << hook
15
- end
16
-
17
- def self.after_save(opts, &hook)
18
- # This is a back-door assertion to prevent regressions in the module's hook definition behavior
19
- raise "expected `unless: :saved_change_to_id?`" unless opts[:unless] == :saved_change_to_id?
20
-
21
- after_save_hooks << hook
22
- end
23
-
24
- def self.after_destroy(&hook)
25
- after_destroy_hooks << hook
26
- end
27
-
28
- include Journaled::Changes
29
- journal_changes_to :my_heart, as: :change_of_heart
30
-
31
- def trigger_after_create_hooks
32
- after_create_hooks.each { |proc| instance_eval(&proc) }
33
- end
34
-
35
- def trigger_after_save_hooks
36
- after_save_hooks.each { |proc| instance_eval(&proc) }
37
- end
38
-
39
- def trigger_after_destroy_hooks
40
- after_destroy_hooks.each { |proc| instance_eval(&proc) }
41
- end
42
- end
43
- end
44
-
45
- subject { klass.new }
46
-
47
- let(:change_writer) { double(Journaled::ChangeWriter, create: true, update: true, delete: true) }
48
-
49
- before do
50
- allow(Journaled::ChangeWriter).to receive(:new) do |opts|
51
- expect(opts[:model]).to eq(subject)
52
- expect(opts[:change_definition].logical_operation).to eq(:change_of_heart)
53
- change_writer
54
- end
55
- end
56
-
57
- it "can be asserted on with our matcher" do
58
- expect(klass).to journal_changes_to(:my_heart, as: :change_of_heart)
59
-
60
- expect(klass).not_to journal_changes_to(:foobaloo, as: :an_event_to_remember)
61
-
62
- expect {
63
- expect(klass).to journal_changes_to(:foobaloo, as: :an_event_to_remember)
64
- }.to raise_error(/> to journal changes to :foobaloo as :an_event_to_remember/)
65
-
66
- expect {
67
- expect(klass).not_to journal_changes_to(:my_heart, as: :change_of_heart)
68
- }.to raise_error(/> not to journal changes to :my_heart as :change_of_heart/)
69
- end
70
-
71
- it "has a single change definition" do
72
- expect(klass._journaled_change_definitions.length).to eq 1
73
- end
74
-
75
- it "journals create events on create" do
76
- subject.trigger_after_create_hooks
77
-
78
- expect(change_writer).to have_received(:create)
79
- expect(Journaled::ChangeWriter).to have_received(:new)
80
- end
81
-
82
- it "journals update events on save" do
83
- subject.trigger_after_save_hooks
84
-
85
- expect(change_writer).to have_received(:update)
86
- expect(Journaled::ChangeWriter).to have_received(:new)
87
- end
88
-
89
- it "journals delete events on destroy" do
90
- subject.trigger_after_destroy_hooks
91
-
92
- expect(change_writer).to have_received(:delete)
93
- expect(Journaled::ChangeWriter).to have_received(:new)
94
- end
95
-
96
- context 'when DJ opts are provided' do
97
- before do
98
- klass.journal_changes_to :thing, as: :other_thing, enqueue_with: { asdf: 1, foo: 'bar' }
99
- end
100
-
101
- it 'sets them on the model' do
102
- expect(klass.journaled_enqueue_opts).to eq(asdf: 1, foo: 'bar')
103
- expect(klass.new.journaled_enqueue_opts).to eq(asdf: 1, foo: 'bar')
104
- end
105
- end
106
- end
@@ -1,109 +0,0 @@
1
- require 'rails_helper'
2
-
3
- if Rails::VERSION::MAJOR > 5 || (Rails::VERSION::MAJOR == 5 && Rails::VERSION::MINOR >= 2)
4
- # rubocop:disable Rails/SkipsModelValidations
5
- RSpec.describe "Raw database change protection" do
6
- let(:journaled_class) do
7
- Class.new(ActiveRecord::Base) do
8
- include Journaled::Changes
9
-
10
- self.table_name = 'widgets'
11
-
12
- journal_changes_to :name, as: :attempt
13
- end
14
- end
15
-
16
- let(:journaled_class_with_no_journaled_columns) do
17
- Class.new(ActiveRecord::Base) do
18
- include Journaled::Changes
19
-
20
- self.table_name = 'widgets'
21
- end
22
- end
23
-
24
- describe "the relation" do
25
- describe "#update_all" do
26
- it "refuses on journaled columns passed as hash" do
27
- expect { journaled_class.update_all(name: nil) }.to raise_error(/aborted by Journaled/)
28
- end
29
-
30
- it "refuses on journaled columns passed as string" do
31
- expect { journaled_class.update_all("\"name\" = NULL") }.to raise_error(/aborted by Journaled/)
32
- expect { journaled_class.update_all("name = null") }.to raise_error(/aborted by Journaled/)
33
- expect { journaled_class.update_all("widgets.name = null") }.to raise_error(/aborted by Journaled/)
34
- expect { journaled_class.update_all("other_column = 'name'") }.not_to raise_error
35
- end
36
-
37
- it "succeeds on unjournaled columns" do
38
- expect { journaled_class.update_all(other_column: "") }.not_to raise_error
39
- end
40
-
41
- it "succeeds when forced on journaled columns" do
42
- expect { journaled_class.update_all({ name: nil }, force: true) }.not_to raise_error
43
- end
44
- end
45
-
46
- describe "#delete" do
47
- it "refuses if journaled columns exist" do
48
- expect { journaled_class.delete(1) }.to raise_error(/aborted by Journaled/)
49
- end
50
-
51
- it "succeeds if no journaled columns exist" do
52
- expect { journaled_class_with_no_journaled_columns.delete(1) }.not_to raise_error
53
- end
54
-
55
- it "succeeds if journaled columns exist when forced" do
56
- expect { journaled_class.delete(1, force: true) }.not_to raise_error
57
- end
58
- end
59
-
60
- describe "#delete_all" do
61
- it "refuses if journaled columns exist" do
62
- expect { journaled_class.delete_all }.to raise_error(/aborted by Journaled/)
63
- end
64
-
65
- it "succeeds if no journaled columns exist" do
66
- expect { journaled_class_with_no_journaled_columns.delete_all }.not_to raise_error
67
- end
68
-
69
- it "succeeds if journaled columns exist when forced" do
70
- expect { journaled_class.delete_all(force: true) }.not_to raise_error
71
- end
72
- end
73
- end
74
-
75
- describe "an instance" do
76
- subject { journaled_class.create!(name: 'foo') }
77
-
78
- describe "#update_columns" do
79
- it "refuses on journaled columns" do
80
- expect { subject.update_columns(name: nil) }.to raise_error(/aborted by Journaled/)
81
- end
82
-
83
- it "succeeds on unjournaled columns" do
84
- expect { subject.update_columns(other_column: "") }.not_to raise_error
85
- end
86
-
87
- it "succeeds when forced on journaled columns" do
88
- expect { subject.update_columns({ name: nil }, force: true) }.not_to raise_error
89
- end
90
- end
91
-
92
- describe "#delete" do
93
- it "refuses if journaled columns exist" do
94
- expect { subject.delete }.to raise_error(/aborted by Journaled/)
95
- end
96
-
97
- it "succeeds if no journaled columns exist" do
98
- instance = journaled_class_with_no_journaled_columns.create!
99
- expect { instance.delete }.not_to raise_error
100
- end
101
-
102
- it "succeeds if journaled columns exist when forced" do
103
- expect { subject.delete(force: true) }.not_to raise_error
104
- end
105
- end
106
- end
107
- end
108
- # rubocop:enable Rails/SkipsModelValidations
109
- end
@@ -1,42 +0,0 @@
1
- require 'rails_helper'
2
-
3
- RSpec.describe Journaled::ActorUriProvider do
4
- describe "#actor_uri" do
5
- let(:current_attributes) { double(:[] => nil) }
6
- let(:actor) { double(to_global_id: actor_gid) }
7
- let(:actor_gid) { double(to_s: "my_fancy_gid") }
8
- let(:program_name) { "/usr/local/bin/puma_or_something" }
9
-
10
- subject { described_class.instance }
11
-
12
- around do |example|
13
- orig_program_name = $PROGRAM_NAME
14
- $PROGRAM_NAME = program_name
15
- example.run
16
- $PROGRAM_NAME = orig_program_name
17
- end
18
-
19
- before do
20
- allow(Journaled::Current.instance)
21
- .to receive(:attributes).and_return(current_attributes)
22
- end
23
-
24
- it "returns the global ID of the entity returned by Current.journaled_actor_proc.call if set" do
25
- allow(current_attributes).to receive(:[]).and_return(-> { actor })
26
- expect(subject.actor_uri).to eq("my_fancy_gid")
27
- expect(current_attributes).to have_received(:[]).with(:journaled_actor_proc)
28
- end
29
-
30
- context "when running in rake" do
31
- let(:program_name) { "rake" }
32
- it "slurps up command line username if available" do
33
- allow(Etc).to receive(:getlogin).and_return("my_unix_username")
34
- expect(subject.actor_uri).to eq("gid://local/my_unix_username")
35
- end
36
- end
37
-
38
- it "falls back to printing out a GID of bare app name" do
39
- expect(subject.actor_uri).to eq("gid://dummy")
40
- end
41
- end
42
- end
@@ -1,281 +0,0 @@
1
- require 'rails_helper'
2
-
3
- RSpec.describe Journaled::ChangeWriter do
4
- let(:model) do
5
- now = Time.zone.now
6
- double(
7
- "Soldier",
8
- id: 28_473,
9
- class: model_class,
10
- attributes: {
11
- "name" => "bob",
12
- "rank" => "first lieutenant",
13
- "serial_number" => "foobar",
14
- "last_sign_in_at" => now,
15
- },
16
- saved_changes: {
17
- "name" => %w(bill bob),
18
- "last_sign_in_at" => now,
19
- },
20
- journaled_enqueue_opts: {},
21
- )
22
- end
23
-
24
- let(:model_class) do
25
- double(
26
- "SoldierClass",
27
- table_name: "soldiers",
28
- attribute_names: %w(id name rank serial_number last_sign_in_at),
29
- )
30
- end
31
-
32
- let(:change_definition) do
33
- Journaled::ChangeDefinition.new(
34
- attribute_names: %i(name rank serial_number),
35
- logical_operation: "identity_change",
36
- )
37
- end
38
-
39
- let(:faulty_change_definition) do
40
- Journaled::ChangeDefinition.new(
41
- attribute_names: %i(name rank serial_number nonexistent_thingie),
42
- logical_operation: "identity_change",
43
- )
44
- end
45
-
46
- subject { described_class.new(model: model, change_definition: change_definition) }
47
-
48
- it "fails to instantiate with an undefined attribute_name" do
49
- expect { described_class.new(model: model, change_definition: faulty_change_definition) }.to raise_error(/\bnonexistent_thingie\n/)
50
- end
51
-
52
- describe "#relevant_attributes" do
53
- let(:model) do
54
- double(
55
- "Soldier",
56
- id: 28_473,
57
- class: model_class,
58
- attributes: {
59
- "name" => "bill",
60
- "rank" => "first lieutenant",
61
- "serial_number" => "foobar",
62
- "last_sign_in_at" => Time.zone.now,
63
- },
64
- saved_changes: {},
65
- journaled_enqueue_opts: {},
66
- )
67
- end
68
-
69
- it "returns all relevant attributes regardless of saved changes" do
70
- expect(subject.relevant_attributes).to eq(
71
- "name" => "bill",
72
- "rank" => "first lieutenant",
73
- "serial_number" => "foobar",
74
- )
75
- end
76
- end
77
-
78
- describe "#relevant_unperturbed_attributes" do
79
- let(:model) do
80
- double(
81
- "Soldier",
82
- id: 28_473,
83
- class: model_class,
84
- attributes: {
85
- "name" => "bill",
86
- "rank" => "first lieutenant",
87
- "serial_number" => "foobar",
88
- "last_sign_in_at" => Time.zone.now,
89
- },
90
- changes: {
91
- "name" => %w(bob bill),
92
- },
93
- journaled_enqueue_opts: {},
94
- )
95
- end
96
-
97
- it "returns the pre-change value of the attributes, regardless of whether they changed" do
98
- expect(subject.relevant_unperturbed_attributes).to eq(
99
- "name" => "bob",
100
- "rank" => "first lieutenant",
101
- "serial_number" => "foobar",
102
- )
103
- end
104
- end
105
-
106
- describe "#relevant_changed_attributes" do
107
- it "returns only relevant changes" do
108
- expect(subject.relevant_changed_attributes).to eq("name" => "bob")
109
- end
110
- end
111
-
112
- describe "#actor_uri" do
113
- it "delegates to ActorUriProvider" do
114
- allow(Journaled::ActorUriProvider).to receive(:instance).and_return(double(actor_uri: "my actor uri"))
115
- expect(Journaled.actor_uri).to eq "my actor uri"
116
- end
117
- end
118
-
119
- describe "#journaled_change_for" do
120
- it "stores passed changes serialized to json" do
121
- expect(subject.journaled_change_for("update", "name" => "bob").changes).to eq('{"name":"bob"}')
122
- end
123
-
124
- it "stores the model's table_name" do
125
- expect(subject.journaled_change_for("update", {}).table_name).to eq("soldiers")
126
- end
127
-
128
- it "converts the model's record_id to a string" do
129
- expect(subject.journaled_change_for("update", {}).record_id).to eq("28473")
130
- end
131
-
132
- it "stuffs the database operation directly" do
133
- expect(subject.journaled_change_for("update", {}).database_operation).to eq("update")
134
- expect(subject.journaled_change_for("delete", {}).database_operation).to eq("delete")
135
- end
136
-
137
- it "includes logical_operation" do
138
- expect(subject.journaled_change_for("update", {}).logical_operation).to eq("identity_change")
139
- end
140
-
141
- it "doesn't set journaled_stream_name if model class doesn't respond to it" do
142
- expect(subject.journaled_change_for("update", {}).journaled_stream_name).to be_nil
143
- end
144
-
145
- context "with journaled default app name set" do
146
- around do |example|
147
- orig_app_name = Journaled.default_stream_name
148
- Journaled.default_stream_name = "foo"
149
- example.run
150
- Journaled.default_stream_name = orig_app_name
151
- end
152
-
153
- it "passes through default" do
154
- expect(subject.journaled_change_for("update", {}).journaled_stream_name).to eq("foo")
155
- end
156
- end
157
-
158
- context "when model class defines journaled_stream_name" do
159
- before do
160
- allow(model_class).to receive(:journaled_stream_name).and_return("my_app_events")
161
- end
162
-
163
- it "sets journaled_stream_name if model_class responds to it" do
164
- expect(subject.journaled_change_for("update", {}).journaled_stream_name).to eq("my_app_events")
165
- end
166
- end
167
- end
168
-
169
- context "with journaling stubbed" do
170
- let(:journaled_change) { instance_double(Journaled::Change, journal!: true) }
171
-
172
- before do
173
- allow(Journaled::Change).to receive(:new).and_return(nil) # must be restubbed to work in context
174
- end
175
-
176
- describe "#create" do
177
- let(:model) do
178
- double(
179
- "Soldier",
180
- id: 28_473,
181
- class: model_class,
182
- attributes: {
183
- "name" => "bill",
184
- "rank" => "first lieutenant",
185
- "serial_number" => "foobar",
186
- "last_sign_in_at" => Time.zone.now,
187
- },
188
- saved_changes: {},
189
- journaled_enqueue_opts: {},
190
- )
191
- end
192
-
193
- it "always journals all relevant attributes, even if unchanged" do
194
- allow(Journaled::Change).to receive(:new) do |opts|
195
- expect(opts[:changes]).to eq '{"name":"bill","rank":"first lieutenant","serial_number":"foobar"}'
196
- journaled_change
197
- end
198
-
199
- subject.create
200
-
201
- expect(Journaled::Change).to have_received(:new)
202
- expect(journaled_change).to have_received(:journal!)
203
- end
204
- end
205
-
206
- describe "#update" do
207
- it "journals only relevant changes" do
208
- allow(Journaled::Change).to receive(:new) do |opts|
209
- expect(opts[:changes]).to eq '{"name":"bob"}'
210
- journaled_change
211
- end
212
-
213
- subject.update
214
-
215
- expect(Journaled::Change).to have_received(:new)
216
- expect(journaled_change).to have_received(:journal!)
217
- end
218
-
219
- context "with no changes" do
220
- let(:model) do
221
- double(
222
- "Soldier",
223
- id: 28_473,
224
- class: model_class,
225
- attributes: {
226
- "name" => "bill",
227
- "rank" => "first lieutenant",
228
- "serial_number" => "foobar",
229
- "last_sign_in_at" => Time.zone.now,
230
- },
231
- saved_changes: {},
232
- )
233
- end
234
-
235
- it "doesn't journal" do
236
- subject.update
237
-
238
- expect(Journaled::Change).not_to have_received(:new)
239
- expect(journaled_change).not_to have_received(:journal!)
240
- end
241
- end
242
- end
243
-
244
- describe "#delete" do
245
- let(:model) do
246
- now = Time.zone.now
247
- double(
248
- "Soldier",
249
- id: 28_473,
250
- class: model_class,
251
- attributes: {
252
- "name" => "bob",
253
- "rank" => "first lieutenant",
254
- "serial_number" => "foobar",
255
- "last_sign_in_at" => now,
256
- },
257
- changes: {
258
- "name" => %w(bill bob),
259
- },
260
- journaled_enqueue_opts: {},
261
- )
262
- end
263
-
264
- it "journals the unperturbed values of all relevant attributes" do
265
- allow(Journaled::Change).to receive(:new) do |opts|
266
- expect(JSON.parse(opts[:changes])).to eq(
267
- "name" => "bill",
268
- "rank" => "first lieutenant",
269
- "serial_number" => "foobar",
270
- )
271
- journaled_change
272
- end
273
-
274
- subject.delete
275
-
276
- expect(Journaled::Change).to have_received(:new)
277
- expect(journaled_change).to have_received(:journal!)
278
- end
279
- end
280
- end
281
- end