journaled 2.5.0 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +115 -24
- data/Rakefile +7 -1
- data/app/controllers/concerns/journaled/actor.rb +8 -5
- data/app/jobs/journaled/application_job.rb +4 -0
- data/app/jobs/journaled/delivery_job.rb +105 -0
- data/app/models/journaled/actor_uri_provider.rb +1 -2
- data/app/models/journaled/change.rb +3 -3
- data/app/models/journaled/change_writer.rb +5 -5
- data/app/models/journaled/event.rb +25 -3
- data/app/models/journaled/writer.rb +17 -14
- data/journaled_schemas/tagged_event.json +14 -0
- data/lib/journaled/current.rb +18 -0
- data/lib/journaled/engine.rb +5 -0
- data/lib/journaled/version.rb +1 -1
- data/lib/journaled.rb +35 -5
- data/spec/dummy/config/application.rb +1 -2
- data/spec/dummy/config/database.yml +4 -19
- data/spec/dummy/config/environments/development.rb +0 -13
- data/spec/dummy/config/environments/test.rb +3 -5
- data/spec/dummy/db/schema.rb +3 -16
- data/spec/{models/journaled/delivery_spec.rb → jobs/journaled/delivery_job_spec.rb} +79 -25
- data/spec/lib/journaled_spec.rb +39 -0
- data/spec/models/concerns/journaled/actor_spec.rb +8 -7
- data/spec/models/database_change_protection_spec.rb +19 -25
- data/spec/models/journaled/actor_uri_provider_spec.rb +6 -5
- data/spec/models/journaled/change_writer_spec.rb +10 -10
- data/spec/models/journaled/event_spec.rb +82 -6
- data/spec/models/journaled/writer_spec.rb +47 -15
- data/spec/rails_helper.rb +1 -2
- data/spec/spec_helper.rb +1 -3
- metadata +29 -84
- data/app/models/journaled/delivery.rb +0 -88
- data/config/routes.rb +0 -2
- data/lib/journaled/enqueue.rb +0 -13
- data/spec/dummy/config/environments/production.rb +0 -78
- data/spec/dummy/config/initializers/assets.rb +0 -8
- data/spec/dummy/db/migrate/20180606205114_create_delayed_jobs.rb +0 -18
- data/spec/support/delayed_job_spec_helper.rb +0 -11
@@ -1,21 +1,6 @@
|
|
1
|
-
default: &default
|
2
|
-
pool: 5
|
3
|
-
encoding: unicode
|
4
|
-
|
5
|
-
postgresql:
|
6
|
-
default: &postgres_default
|
7
|
-
adapter: postgresql
|
8
|
-
url: <%= ENV['DATABASE_URL'] %>
|
9
|
-
test: &postgres_test
|
10
|
-
<<: *postgres_default
|
11
|
-
url: <%= ENV['DATABASE_URL'] || "postgresql://localhost:#{ENV.fetch('PGPORT', 5432)}/journaled_test" %>
|
12
|
-
database: journaled_test
|
13
|
-
development: &postgres_development
|
14
|
-
<<: *postgres_default
|
15
|
-
url: <%= ENV['DATABASE_URL'] || "postgresql://localhost:#{ENV.fetch('PGPORT', 5432)}/journaled_development" %>
|
16
|
-
database: journaled_development
|
17
|
-
|
18
1
|
development:
|
19
|
-
|
2
|
+
adapter: sqlite3
|
3
|
+
database: ":memory:"
|
20
4
|
test:
|
21
|
-
|
5
|
+
adapter: sqlite3
|
6
|
+
database: ":memory:"
|
@@ -13,25 +13,12 @@ Rails.application.configure do
|
|
13
13
|
config.consider_all_requests_local = true
|
14
14
|
config.action_controller.perform_caching = false
|
15
15
|
|
16
|
-
# Don't care if the mailer can't send.
|
17
|
-
config.action_mailer.raise_delivery_errors = false
|
18
|
-
|
19
16
|
# Print deprecation notices to the Rails logger.
|
20
17
|
config.active_support.deprecation = :log
|
21
18
|
|
22
19
|
# Raise an error on page load if there are pending migrations.
|
23
20
|
config.active_record.migration_error = :page_load
|
24
21
|
|
25
|
-
# Debug mode disables concatenation and preprocessing of assets.
|
26
|
-
# This option may cause significant delays in view rendering with a large
|
27
|
-
# number of complex assets.
|
28
|
-
config.assets.debug = true
|
29
|
-
|
30
|
-
# Adds additional error checking when serving assets at runtime.
|
31
|
-
# Checks for improperly declared sprockets dependencies.
|
32
|
-
# Raises helpful error messages.
|
33
|
-
config.assets.raise_runtime_errors = true
|
34
|
-
|
35
22
|
# Raises error for missing translations
|
36
23
|
# config.action_view.raise_on_missing_translations = true
|
37
24
|
end
|
@@ -26,14 +26,12 @@ Rails.application.configure do
|
|
26
26
|
# Disable request forgery protection in test environment.
|
27
27
|
config.action_controller.allow_forgery_protection = false
|
28
28
|
|
29
|
-
# Tell Action Mailer not to deliver emails to the real world.
|
30
|
-
# The :test delivery method accumulates sent emails in the
|
31
|
-
# ActionMailer::Base.deliveries array.
|
32
|
-
config.action_mailer.delivery_method = :test
|
33
|
-
|
34
29
|
# Print deprecation notices to the stderr.
|
35
30
|
config.active_support.deprecation = :stderr
|
36
31
|
|
37
32
|
# Raises error for missing translations
|
38
33
|
# config.action_view.raise_on_missing_translations = true
|
34
|
+
|
35
|
+
# Use ActiveJob test adapter
|
36
|
+
config.active_job.queue_adapter = :test
|
39
37
|
end
|
data/spec/dummy/db/schema.rb
CHANGED
@@ -11,21 +11,8 @@
|
|
11
11
|
# It's strongly recommended that you check this file into your version control system.
|
12
12
|
|
13
13
|
ActiveRecord::Schema.define(version: 20180606205114) do
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
create_table "delayed_jobs", force: :cascade do |t|
|
18
|
-
t.integer "priority", default: 0, null: false
|
19
|
-
t.integer "attempts", default: 0, null: false
|
20
|
-
t.text "handler", null: false
|
21
|
-
t.text "last_error"
|
22
|
-
t.datetime "run_at"
|
23
|
-
t.datetime "locked_at"
|
24
|
-
t.datetime "failed_at"
|
25
|
-
t.string "locked_by"
|
26
|
-
t.string "queue"
|
27
|
-
t.datetime "created_at"
|
28
|
-
t.datetime "updated_at"
|
29
|
-
t.index ["priority", "run_at"], name: "delayed_jobs_priority"
|
14
|
+
create_table "widgets", force: :cascade do |t|
|
15
|
+
t.string "name"
|
16
|
+
t.string "other_column"
|
30
17
|
end
|
31
18
|
end
|
@@ -1,16 +1,11 @@
|
|
1
1
|
require 'rails_helper'
|
2
2
|
|
3
|
-
RSpec.describe Journaled::
|
3
|
+
RSpec.describe Journaled::DeliveryJob do
|
4
4
|
let(:stream_name) { 'test_events' }
|
5
5
|
let(:partition_key) { 'fake_partition_key' }
|
6
6
|
let(:serialized_event) { '{"foo":"bar"}' }
|
7
7
|
let(:kinesis_client) { Aws::Kinesis::Client.new(stub_responses: true) }
|
8
|
-
|
9
|
-
around do |example|
|
10
|
-
with_env(JOURNALED_STREAM_NAME: stream_name) { example.run }
|
11
|
-
end
|
12
|
-
|
13
|
-
subject { described_class.new serialized_event: serialized_event, partition_key: partition_key, app_name: nil }
|
8
|
+
let(:args) { { serialized_event: serialized_event, partition_key: partition_key, stream_name: stream_name } }
|
14
9
|
|
15
10
|
describe '#perform' do
|
16
11
|
let(:return_status_body) { { shard_id: '101', sequence_number: '101123' } }
|
@@ -20,15 +15,21 @@ RSpec.describe Journaled::Delivery do
|
|
20
15
|
allow(Aws::AssumeRoleCredentials).to receive(:new).and_call_original
|
21
16
|
allow(Aws::Kinesis::Client).to receive(:new).and_return kinesis_client
|
22
17
|
kinesis_client.stub_responses(:put_record, return_status_body)
|
18
|
+
allow(kinesis_client).to receive(:put_record).and_call_original
|
23
19
|
|
24
20
|
allow(Journaled).to receive(:enabled?).and_return(true)
|
25
21
|
end
|
26
22
|
|
27
23
|
it 'makes requests to AWS to put the event on the Kinesis with the correct body' do
|
28
|
-
event =
|
24
|
+
event = described_class.perform_now(args)
|
29
25
|
|
30
26
|
expect(event.shard_id).to eq '101'
|
31
27
|
expect(event.sequence_number).to eq '101123'
|
28
|
+
expect(kinesis_client).to have_received(:put_record).with(
|
29
|
+
stream_name: 'test_events',
|
30
|
+
data: '{"foo":"bar"}',
|
31
|
+
partition_key: 'fake_partition_key',
|
32
|
+
)
|
32
33
|
end
|
33
34
|
|
34
35
|
context 'when JOURNALED_IAM_ROLE_ARN is defined' do
|
@@ -59,7 +60,7 @@ RSpec.describe Journaled::Delivery do
|
|
59
60
|
end
|
60
61
|
|
61
62
|
it 'initializes a Kinesis client with assume role credentials' do
|
62
|
-
|
63
|
+
described_class.perform_now(args)
|
63
64
|
|
64
65
|
expect(Aws::AssumeRoleCredentials).to have_received(:new).with(
|
65
66
|
client: aws_sts_client,
|
@@ -69,11 +70,64 @@ RSpec.describe Journaled::Delivery do
|
|
69
70
|
end
|
70
71
|
end
|
71
72
|
|
72
|
-
context 'when the stream name
|
73
|
+
context 'when the stream name is not set' do
|
73
74
|
let(:stream_name) { nil }
|
74
75
|
|
75
76
|
it 'raises an KeyError error' do
|
76
|
-
expect {
|
77
|
+
expect { described_class.perform_now(args) }.to raise_error ArgumentError, 'missing keyword: stream_name'
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
unless Gem::Version.new(Journaled::VERSION) < Gem::Version.new('5.0.0')
|
82
|
+
raise <<~MSG
|
83
|
+
Hey! I see that you're bumping the version to 5.0!
|
84
|
+
|
85
|
+
This is a reminder to:
|
86
|
+
- remove the `app_name` argument (and related logic) from `Journaled::DeliveryJob`,
|
87
|
+
- remove the following app_name test contexts, and
|
88
|
+
- make `stream_name` a required kwarg
|
89
|
+
|
90
|
+
Thanks!
|
91
|
+
MSG
|
92
|
+
end
|
93
|
+
|
94
|
+
context 'when the legacy app_name argument is present but nil' do
|
95
|
+
let(:args) { { serialized_event: serialized_event, partition_key: partition_key, app_name: nil } }
|
96
|
+
|
97
|
+
around do |example|
|
98
|
+
with_env(JOURNALED_STREAM_NAME: 'legacy_stream_name') { example.run }
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'makes requests to AWS to put the event on the Kinesis with the correct body' do
|
102
|
+
event = described_class.perform_now(args)
|
103
|
+
|
104
|
+
expect(event.shard_id).to eq '101'
|
105
|
+
expect(event.sequence_number).to eq '101123'
|
106
|
+
expect(kinesis_client).to have_received(:put_record).with(
|
107
|
+
stream_name: 'legacy_stream_name',
|
108
|
+
data: '{"foo":"bar"}',
|
109
|
+
partition_key: 'fake_partition_key',
|
110
|
+
)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
context 'when the legacy app_name argument is present and has a value' do
|
115
|
+
let(:args) { { serialized_event: serialized_event, partition_key: partition_key, app_name: 'pied_piper' } }
|
116
|
+
|
117
|
+
around do |example|
|
118
|
+
with_env(PIED_PIPER_JOURNALED_STREAM_NAME: 'pied_piper_events') { example.run }
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'makes requests to AWS to put the event on the Kinesis with the correct body' do
|
122
|
+
event = described_class.perform_now(args)
|
123
|
+
|
124
|
+
expect(event.shard_id).to eq '101'
|
125
|
+
expect(event.sequence_number).to eq '101123'
|
126
|
+
expect(kinesis_client).to have_received(:put_record).with(
|
127
|
+
stream_name: 'pied_piper_events',
|
128
|
+
data: '{"foo":"bar"}',
|
129
|
+
partition_key: 'fake_partition_key',
|
130
|
+
)
|
77
131
|
end
|
78
132
|
end
|
79
133
|
|
@@ -83,8 +137,11 @@ RSpec.describe Journaled::Delivery do
|
|
83
137
|
end
|
84
138
|
|
85
139
|
it 'catches the error and re-raises a subclass of NotTrulyExceptionalError and logs about the failure' do
|
86
|
-
|
87
|
-
expect {
|
140
|
+
allow(Rails.logger).to receive(:error)
|
141
|
+
expect { described_class.perform_now(args) }.to raise_error described_class::KinesisTemporaryFailure
|
142
|
+
expect(Rails.logger).to have_received(:error).with(
|
143
|
+
"Kinesis Error - Server Error occurred - Aws::Kinesis::Errors::InternalFailure",
|
144
|
+
).once
|
88
145
|
end
|
89
146
|
end
|
90
147
|
|
@@ -95,7 +152,7 @@ RSpec.describe Journaled::Delivery do
|
|
95
152
|
|
96
153
|
it 'catches the error and re-raises a subclass of NotTrulyExceptionalError and logs about the failure' do
|
97
154
|
allow(Rails.logger).to receive(:error)
|
98
|
-
expect {
|
155
|
+
expect { described_class.perform_now(args) }.to raise_error described_class::KinesisTemporaryFailure
|
99
156
|
expect(Rails.logger).to have_received(:error).with(/\AKinesis Error/).once
|
100
157
|
end
|
101
158
|
end
|
@@ -106,7 +163,7 @@ RSpec.describe Journaled::Delivery do
|
|
106
163
|
end
|
107
164
|
|
108
165
|
it 'raises an error that subclasses Aws::Kinesis::Errors::ServiceError' do
|
109
|
-
expect {
|
166
|
+
expect { described_class.perform_now(args) }.to raise_error Aws::Kinesis::Errors::ServiceError
|
110
167
|
end
|
111
168
|
end
|
112
169
|
|
@@ -116,7 +173,7 @@ RSpec.describe Journaled::Delivery do
|
|
116
173
|
end
|
117
174
|
|
118
175
|
it 'raises an AccessDeniedException error' do
|
119
|
-
expect {
|
176
|
+
expect { described_class.perform_now(args) }.to raise_error Aws::Kinesis::Errors::AccessDeniedException
|
120
177
|
end
|
121
178
|
end
|
122
179
|
|
@@ -126,31 +183,28 @@ RSpec.describe Journaled::Delivery do
|
|
126
183
|
end
|
127
184
|
|
128
185
|
it 'catches the error and re-raises a subclass of NotTrulyExceptionalError and logs about the failure' do
|
129
|
-
|
186
|
+
allow(Rails.logger).to receive(:error)
|
187
|
+
expect { described_class.perform_now(args) }.to raise_error described_class::KinesisTemporaryFailure
|
188
|
+
expect(Rails.logger).to have_received(:error).with(
|
130
189
|
"Kinesis Error - Networking Error occurred - Seahorse::Client::NetworkingError",
|
131
190
|
).once
|
132
|
-
expect { subject.perform }.to raise_error described_class::KinesisTemporaryFailure
|
133
191
|
end
|
134
192
|
end
|
135
193
|
end
|
136
194
|
|
137
|
-
describe "
|
195
|
+
describe ".legacy_computed_stream_name" do
|
138
196
|
context "when app_name is unspecified" do
|
139
|
-
subject { described_class.new serialized_event: serialized_event, partition_key: partition_key, app_name: nil }
|
140
|
-
|
141
197
|
it "is fetched from a prefixed ENV var if specified" do
|
142
198
|
allow(ENV).to receive(:fetch).and_return("expected_stream_name")
|
143
|
-
expect(
|
199
|
+
expect(described_class.legacy_computed_stream_name(app_name: nil)).to eq("expected_stream_name")
|
144
200
|
expect(ENV).to have_received(:fetch).with("JOURNALED_STREAM_NAME")
|
145
201
|
end
|
146
202
|
end
|
147
203
|
|
148
204
|
context "when app_name is specified" do
|
149
|
-
subject { described_class.new serialized_event: serialized_event, partition_key: partition_key, app_name: "my_funky_app_name" }
|
150
|
-
|
151
205
|
it "is fetched from a prefixed ENV var if specified" do
|
152
206
|
allow(ENV).to receive(:fetch).and_return("expected_stream_name")
|
153
|
-
expect(
|
207
|
+
expect(described_class.legacy_computed_stream_name(app_name: "my_funky_app_name")).to eq("expected_stream_name")
|
154
208
|
expect(ENV).to have_received(:fetch).with("MY_FUNKY_APP_NAME_JOURNALED_STREAM_NAME")
|
155
209
|
end
|
156
210
|
end
|
data/spec/lib/journaled_spec.rb
CHANGED
@@ -49,4 +49,43 @@ RSpec.describe Journaled do
|
|
49
49
|
expect(described_class.actor_uri).to eq "my actor uri"
|
50
50
|
end
|
51
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
|
+
begin
|
79
|
+
example.run
|
80
|
+
ensure
|
81
|
+
ActiveJob::Base.queue_adapter = :test
|
82
|
+
ActiveJob::Base.enable_test_adapter(ActiveJob::QueueAdapters::TestAdapter.new)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'does not raise an error' do
|
87
|
+
expect { described_class.detect_queue_adapter! }.not_to raise_error
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
52
91
|
end
|
@@ -5,11 +5,10 @@ RSpec.describe Journaled::Actor do
|
|
5
5
|
let(:user) { double("User") }
|
6
6
|
let(:klass) do
|
7
7
|
Class.new do
|
8
|
-
cattr_accessor
|
9
|
-
self.before_actions = []
|
8
|
+
cattr_accessor(:before_actions) { [] }
|
10
9
|
|
11
|
-
def self.before_action(
|
12
|
-
before_actions <<
|
10
|
+
def self.before_action(method_name, _opts)
|
11
|
+
before_actions << method_name
|
13
12
|
end
|
14
13
|
|
15
14
|
include Journaled::Actor
|
@@ -21,7 +20,7 @@ RSpec.describe Journaled::Actor do
|
|
21
20
|
end
|
22
21
|
|
23
22
|
def trigger_before_actions
|
24
|
-
before_actions.each { |
|
23
|
+
before_actions.each { |method_name| send(method_name) }
|
25
24
|
end
|
26
25
|
end
|
27
26
|
end
|
@@ -33,7 +32,8 @@ RSpec.describe Journaled::Actor do
|
|
33
32
|
|
34
33
|
allow(subject).to receive(:current_user).and_return(nil)
|
35
34
|
|
36
|
-
expect(
|
35
|
+
expect(Journaled::Current.journaled_actor_proc.call).to eq nil
|
36
|
+
expect(Journaled::Current.actor).to eq nil
|
37
37
|
end
|
38
38
|
|
39
39
|
it "Stores a thunk returning current_user if it is set when called" do
|
@@ -41,6 +41,7 @@ RSpec.describe Journaled::Actor do
|
|
41
41
|
|
42
42
|
allow(subject).to receive(:current_user).and_return(user)
|
43
43
|
|
44
|
-
expect(
|
44
|
+
expect(Journaled::Current.journaled_actor_proc.call).to eq user
|
45
|
+
expect(Journaled::Current.actor).to eq user
|
45
46
|
end
|
46
47
|
end
|
@@ -4,38 +4,42 @@ if Rails::VERSION::MAJOR > 5 || (Rails::VERSION::MAJOR == 5 && Rails::VERSION::M
|
|
4
4
|
# rubocop:disable Rails/SkipsModelValidations
|
5
5
|
RSpec.describe "Raw database change protection" do
|
6
6
|
let(:journaled_class) do
|
7
|
-
Class.new(
|
7
|
+
Class.new(ActiveRecord::Base) do
|
8
8
|
include Journaled::Changes
|
9
9
|
|
10
|
-
|
10
|
+
self.table_name = 'widgets'
|
11
|
+
|
12
|
+
journal_changes_to :name, as: :attempt
|
11
13
|
end
|
12
14
|
end
|
13
15
|
|
14
16
|
let(:journaled_class_with_no_journaled_columns) do
|
15
|
-
Class.new(
|
17
|
+
Class.new(ActiveRecord::Base) do
|
16
18
|
include Journaled::Changes
|
19
|
+
|
20
|
+
self.table_name = 'widgets'
|
17
21
|
end
|
18
22
|
end
|
19
23
|
|
20
24
|
describe "the relation" do
|
21
25
|
describe "#update_all" do
|
22
26
|
it "refuses on journaled columns passed as hash" do
|
23
|
-
expect { journaled_class.update_all(
|
27
|
+
expect { journaled_class.update_all(name: nil) }.to raise_error(/aborted by Journaled/)
|
24
28
|
end
|
25
29
|
|
26
30
|
it "refuses on journaled columns passed as string" do
|
27
|
-
expect { journaled_class.update_all("\"
|
28
|
-
expect { journaled_class.update_all("
|
29
|
-
expect { journaled_class.update_all("
|
30
|
-
expect { journaled_class.update_all("
|
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
|
31
35
|
end
|
32
36
|
|
33
37
|
it "succeeds on unjournaled columns" do
|
34
|
-
expect { journaled_class.update_all(
|
38
|
+
expect { journaled_class.update_all(other_column: "") }.not_to raise_error
|
35
39
|
end
|
36
40
|
|
37
41
|
it "succeeds when forced on journaled columns" do
|
38
|
-
expect { journaled_class.update_all({
|
42
|
+
expect { journaled_class.update_all({ name: nil }, force: true) }.not_to raise_error
|
39
43
|
end
|
40
44
|
end
|
41
45
|
|
@@ -69,29 +73,19 @@ if Rails::VERSION::MAJOR > 5 || (Rails::VERSION::MAJOR == 5 && Rails::VERSION::M
|
|
69
73
|
end
|
70
74
|
|
71
75
|
describe "an instance" do
|
72
|
-
|
73
|
-
module TestJob
|
74
|
-
def perform
|
75
|
-
"foo"
|
76
|
-
end
|
77
|
-
|
78
|
-
module_function :perform
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
subject { journaled_class.enqueue(job) }
|
76
|
+
subject { journaled_class.create!(name: 'foo') }
|
83
77
|
|
84
78
|
describe "#update_columns" do
|
85
79
|
it "refuses on journaled columns" do
|
86
|
-
expect { subject.update_columns(
|
80
|
+
expect { subject.update_columns(name: nil) }.to raise_error(/aborted by Journaled/)
|
87
81
|
end
|
88
82
|
|
89
83
|
it "succeeds on unjournaled columns" do
|
90
|
-
expect { subject.update_columns(
|
84
|
+
expect { subject.update_columns(other_column: "") }.not_to raise_error
|
91
85
|
end
|
92
86
|
|
93
87
|
it "succeeds when forced on journaled columns" do
|
94
|
-
expect { subject.update_columns({
|
88
|
+
expect { subject.update_columns({ name: nil }, force: true) }.not_to raise_error
|
95
89
|
end
|
96
90
|
end
|
97
91
|
|
@@ -101,7 +95,7 @@ if Rails::VERSION::MAJOR > 5 || (Rails::VERSION::MAJOR == 5 && Rails::VERSION::M
|
|
101
95
|
end
|
102
96
|
|
103
97
|
it "succeeds if no journaled columns exist" do
|
104
|
-
instance = journaled_class_with_no_journaled_columns.
|
98
|
+
instance = journaled_class_with_no_journaled_columns.create!
|
105
99
|
expect { instance.delete }.not_to raise_error
|
106
100
|
end
|
107
101
|
|
@@ -2,7 +2,7 @@ require 'rails_helper'
|
|
2
2
|
|
3
3
|
RSpec.describe Journaled::ActorUriProvider do
|
4
4
|
describe "#actor_uri" do
|
5
|
-
let(:
|
5
|
+
let(:current_attributes) { double(:[] => nil) }
|
6
6
|
let(:actor) { double(to_global_id: actor_gid) }
|
7
7
|
let(:actor_gid) { double(to_s: "my_fancy_gid") }
|
8
8
|
let(:program_name) { "/usr/local/bin/puma_or_something" }
|
@@ -17,13 +17,14 @@ RSpec.describe Journaled::ActorUriProvider do
|
|
17
17
|
end
|
18
18
|
|
19
19
|
before do
|
20
|
-
allow(
|
20
|
+
allow(Journaled::Current.instance)
|
21
|
+
.to receive(:attributes).and_return(current_attributes)
|
21
22
|
end
|
22
23
|
|
23
|
-
it "returns the global ID of the entity returned by
|
24
|
-
allow(
|
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 })
|
25
26
|
expect(subject.actor_uri).to eq("my_fancy_gid")
|
26
|
-
expect(
|
27
|
+
expect(current_attributes).to have_received(:[]).with(:journaled_actor_proc)
|
27
28
|
end
|
28
29
|
|
29
30
|
context "when running in rake" do
|
@@ -138,30 +138,30 @@ RSpec.describe Journaled::ChangeWriter do
|
|
138
138
|
expect(subject.journaled_change_for("update", {}).logical_operation).to eq("identity_change")
|
139
139
|
end
|
140
140
|
|
141
|
-
it "doesn't set
|
142
|
-
expect(subject.journaled_change_for("update", {}).
|
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 eq(nil)
|
143
143
|
end
|
144
144
|
|
145
145
|
context "with journaled default app name set" do
|
146
146
|
around do |example|
|
147
|
-
orig_app_name = Journaled.
|
148
|
-
Journaled.
|
147
|
+
orig_app_name = Journaled.default_stream_name
|
148
|
+
Journaled.default_stream_name = "foo"
|
149
149
|
example.run
|
150
|
-
Journaled.
|
150
|
+
Journaled.default_stream_name = orig_app_name
|
151
151
|
end
|
152
152
|
|
153
153
|
it "passes through default" do
|
154
|
-
expect(subject.journaled_change_for("update", {}).
|
154
|
+
expect(subject.journaled_change_for("update", {}).journaled_stream_name).to eq("foo")
|
155
155
|
end
|
156
156
|
end
|
157
157
|
|
158
|
-
context "when model class defines
|
158
|
+
context "when model class defines journaled_stream_name" do
|
159
159
|
before do
|
160
|
-
allow(model_class).to receive(:
|
160
|
+
allow(model_class).to receive(:journaled_stream_name).and_return("my_app_events")
|
161
161
|
end
|
162
162
|
|
163
|
-
it "sets
|
164
|
-
expect(subject.journaled_change_for("update", {}).
|
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
165
|
end
|
166
166
|
end
|
167
167
|
end
|
@@ -65,14 +65,14 @@ RSpec.describe Journaled::Event do
|
|
65
65
|
end
|
66
66
|
end
|
67
67
|
|
68
|
-
describe '#
|
68
|
+
describe '#journaled_stream_name' do
|
69
69
|
it 'returns nil in the base class so it can be set explicitly in apps spanning multiple app domains' do
|
70
|
-
expect(sample_journaled_event.
|
70
|
+
expect(sample_journaled_event.journaled_stream_name).to be_nil
|
71
71
|
end
|
72
72
|
|
73
73
|
it 'returns the journaled default if set' do
|
74
|
-
allow(Journaled).to receive(:
|
75
|
-
expect(sample_journaled_event.
|
74
|
+
allow(Journaled).to receive(:default_stream_name).and_return("my_app_events")
|
75
|
+
expect(sample_journaled_event.journaled_stream_name).to eq("my_app_events")
|
76
76
|
end
|
77
77
|
end
|
78
78
|
|
@@ -88,8 +88,10 @@ RSpec.describe Journaled::Event do
|
|
88
88
|
|
89
89
|
context 'when no additional attributes have been defined' do
|
90
90
|
it 'returns the base attributes, and memoizes them after the first call' do
|
91
|
-
expect(sample_journaled_event.journaled_attributes)
|
92
|
-
|
91
|
+
expect(sample_journaled_event.journaled_attributes)
|
92
|
+
.to eq id: fake_uuid, created_at: frozen_time, event_type: 'some_class_name'
|
93
|
+
expect(sample_journaled_event.journaled_attributes)
|
94
|
+
.to eq id: fake_uuid, created_at: frozen_time, event_type: 'some_class_name'
|
93
95
|
end
|
94
96
|
end
|
95
97
|
|
@@ -134,6 +136,80 @@ RSpec.describe Journaled::Event do
|
|
134
136
|
)
|
135
137
|
end
|
136
138
|
end
|
139
|
+
|
140
|
+
context 'tagged: true' do
|
141
|
+
before do
|
142
|
+
sample_journaled_event_class.journal_attributes tagged: true
|
143
|
+
end
|
144
|
+
|
145
|
+
it 'adds a "tags" attribute' do
|
146
|
+
expect(sample_journaled_event.journaled_attributes).to include(tags: {})
|
147
|
+
end
|
148
|
+
|
149
|
+
context 'when tags are specified' do
|
150
|
+
around do |example|
|
151
|
+
Journaled.tag!(foo: 'bar')
|
152
|
+
Journaled.tagged(baz: 'bat') { example.run }
|
153
|
+
end
|
154
|
+
|
155
|
+
it 'adds them to the journaled attributes' do
|
156
|
+
expect(sample_journaled_event.journaled_attributes).to include(
|
157
|
+
tags: { foo: 'bar', baz: 'bat' },
|
158
|
+
)
|
159
|
+
end
|
160
|
+
|
161
|
+
context 'when even more tags are nested' do
|
162
|
+
it 'merges them in and then resets them' do
|
163
|
+
Journaled.tagged(oh_no: 'even more tags') do
|
164
|
+
expect(sample_journaled_event.journaled_attributes).to include(
|
165
|
+
tags: { foo: 'bar', baz: 'bat', oh_no: 'even more tags' },
|
166
|
+
)
|
167
|
+
end
|
168
|
+
|
169
|
+
allow(SecureRandom).to receive(:uuid).and_return(fake_uuid).once
|
170
|
+
expect(sample_journaled_event_class.new.journaled_attributes).to include(
|
171
|
+
tags: { foo: 'bar', baz: 'bat' },
|
172
|
+
)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
context 'when custom event tags are also specified and merged' do
|
177
|
+
let(:sample_journaled_event_class) do
|
178
|
+
Class.new do
|
179
|
+
include Journaled::Event
|
180
|
+
|
181
|
+
def tags
|
182
|
+
super.merge(abc: '123')
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
it 'combines all tags' do
|
188
|
+
expect(sample_journaled_event.journaled_attributes).to include(
|
189
|
+
tags: { foo: 'bar', baz: 'bat', abc: '123' },
|
190
|
+
)
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
context 'when custom event tags are also specified but not merged' do
|
195
|
+
let(:sample_journaled_event_class) do
|
196
|
+
Class.new do
|
197
|
+
include Journaled::Event
|
198
|
+
|
199
|
+
def tags
|
200
|
+
{ bananas: 'are great', but_not_actually: 'the best source of potassium' } # it's true
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
it 'adds them to the journaled attributes' do
|
206
|
+
expect(sample_journaled_event.journaled_attributes).to include(
|
207
|
+
tags: { bananas: 'are great', but_not_actually: 'the best source of potassium' },
|
208
|
+
)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
137
213
|
end
|
138
214
|
|
139
215
|
describe '#journaled_enqueue_opts, .journaled_enqueue_opts' do
|