journaled 4.0.0 → 4.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +179 -53
- data/Rakefile +10 -24
- data/app/controllers/concerns/journaled/actor.rb +8 -5
- data/app/models/concerns/journaled/changes.rb +5 -5
- data/app/models/journaled/actor_uri_provider.rb +1 -2
- data/app/models/journaled/change.rb +12 -12
- data/app/models/journaled/change_writer.rb +3 -2
- data/app/models/journaled/event.rb +24 -2
- data/app/models/journaled/writer.rb +17 -11
- data/journaled_schemas/tagged_event.json +14 -0
- data/lib/journaled/current.rb +18 -0
- data/lib/journaled/relation_change_protection.rb +11 -10
- data/lib/journaled/rspec.rb +86 -0
- data/lib/journaled/version.rb +1 -1
- data/lib/journaled.rb +14 -2
- data/spec/dummy/config.ru +1 -1
- data/spec/jobs/journaled/delivery_job_spec.rb +10 -10
- data/spec/lib/journaled_spec.rb +4 -6
- data/spec/models/concerns/journaled/actor_spec.rb +8 -7
- data/spec/models/concerns/journaled/changes_spec.rb +1 -1
- data/spec/models/journaled/actor_uri_provider_spec.rb +6 -5
- data/spec/models/journaled/change_writer_spec.rb +1 -1
- data/spec/models/journaled/event_spec.rb +78 -2
- data/spec/models/journaled/json_schema_model/validator_spec.rb +6 -6
- data/spec/models/journaled/writer_spec.rb +79 -12
- data/spec/rails_helper.rb +1 -1
- data/spec/spec_helper.rb +4 -0
- metadata +26 -23
data/lib/journaled/rspec.rb
CHANGED
@@ -16,3 +16,89 @@ RSpec::Matchers.define :journal_changes_to do |*attribute_names, as:|
|
|
16
16
|
"expected #{model_class} not to journal changes to #{attribute_names.map(&:inspect).join(', ')} as #{as.inspect}"
|
17
17
|
end
|
18
18
|
end
|
19
|
+
|
20
|
+
RSpec::Matchers.define_negated_matcher :not_journal_changes_to, :journal_changes_to
|
21
|
+
|
22
|
+
RSpec::Matchers.define :journal_events_including do |*expected_events|
|
23
|
+
raise "Please specify at least one expected event. RSpec argument matchers are supported." if expected_events.empty?
|
24
|
+
|
25
|
+
attr_accessor :expected, :actual, :matches, :nonmatches
|
26
|
+
|
27
|
+
chain :with_schema_name, :expected_schema_name
|
28
|
+
chain :with_partition_key, :expected_partition_key
|
29
|
+
chain :with_stream_name, :expected_stream_name
|
30
|
+
chain :with_enqueue_opts, :expected_enqueue_opts
|
31
|
+
chain :with_priority, :expected_priority
|
32
|
+
|
33
|
+
def supports_block_expectations?
|
34
|
+
true
|
35
|
+
end
|
36
|
+
|
37
|
+
def hash_including_recursive(hash)
|
38
|
+
hash_including(
|
39
|
+
hash.transform_values { |v| v.is_a?(Hash) ? hash_including_recursive(v) : v },
|
40
|
+
)
|
41
|
+
end
|
42
|
+
|
43
|
+
match do |block|
|
44
|
+
expected_events = [expected_events.first].flatten(1) unless expected_events.length > 1
|
45
|
+
|
46
|
+
self.expected = expected_events.map { |e| { journaled_attributes: e } }
|
47
|
+
expected.each { |e| e.merge!(journaled_schema_name: expected_schema_name) } if expected_schema_name
|
48
|
+
expected.each { |e| e.merge!(journaled_partition_key: expected_partition_key) } if expected_partition_key
|
49
|
+
expected.each { |e| e.merge!(journaled_stream_name: expected_stream_name) } if expected_stream_name
|
50
|
+
expected.each { |e| e.merge!(journaled_enqueue_opts: expected_enqueue_opts) } if expected_enqueue_opts
|
51
|
+
expected.each { |e| e.merge!(priority: expected_priority) } if expected_priority
|
52
|
+
self.actual = []
|
53
|
+
|
54
|
+
callback = ->(_name, _started, _finished, _unique_id, payload) do
|
55
|
+
event = payload[:event]
|
56
|
+
a = { journaled_attributes: event.journaled_attributes }
|
57
|
+
a[:journaled_schema_name] = event.journaled_schema_name if expected_schema_name
|
58
|
+
a[:journaled_partition_key] = event.journaled_partition_key if expected_partition_key
|
59
|
+
a[:journaled_stream_name] = event.journaled_stream_name if expected_stream_name
|
60
|
+
a[:journaled_enqueue_opts] = event.journaled_enqueue_opts if expected_enqueue_opts
|
61
|
+
a[:priority] = payload[:priority] if expected_priority
|
62
|
+
actual << a
|
63
|
+
end
|
64
|
+
|
65
|
+
ActiveSupport::Notifications.subscribed(callback, 'journaled.event.enqueue', &block)
|
66
|
+
|
67
|
+
self.matches = actual.select do |a|
|
68
|
+
expected.any? { |e| values_match?(hash_including_recursive(e), a) }
|
69
|
+
end
|
70
|
+
|
71
|
+
self.nonmatches = actual - matches
|
72
|
+
|
73
|
+
exact_matches = matches.dup
|
74
|
+
matches.count == expected.count && expected.all? do |e|
|
75
|
+
match, index = exact_matches.each_with_index.find { |a, _| values_match?(hash_including_recursive(e), a) }
|
76
|
+
exact_matches.delete_at(index) if match
|
77
|
+
end && exact_matches.empty?
|
78
|
+
end
|
79
|
+
|
80
|
+
failure_message do
|
81
|
+
<<~MSG
|
82
|
+
Expected the code block to journal exactly one matching event per expected event.
|
83
|
+
|
84
|
+
Expected Events (#{expected.count}):
|
85
|
+
===============================================================================
|
86
|
+
#{expected.map(&:to_json).join("\n ")}
|
87
|
+
===============================================================================
|
88
|
+
|
89
|
+
Matching Events (#{matches.count}):
|
90
|
+
===============================================================================
|
91
|
+
#{matches.map(&:to_json).join("\n ")}
|
92
|
+
===============================================================================
|
93
|
+
|
94
|
+
Non-Matching Events (#{nonmatches.count}):
|
95
|
+
===============================================================================
|
96
|
+
#{nonmatches.map(&:to_json).join("\n ")}
|
97
|
+
===============================================================================
|
98
|
+
MSG
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
RSpec::Matchers.alias_matcher :journal_event_including, :journal_events_including
|
103
|
+
RSpec::Matchers.define_negated_matcher :not_journal_events_including, :journal_events_including
|
104
|
+
RSpec::Matchers.define_negated_matcher :not_journal_event_including, :journal_event_including
|
data/lib/journaled/version.rb
CHANGED
data/lib/journaled.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
require "aws-sdk-kinesis"
|
2
2
|
require "active_job"
|
3
3
|
require "json-schema"
|
4
|
-
require "request_store"
|
5
4
|
|
6
5
|
require "journaled/engine"
|
6
|
+
require "journaled/current"
|
7
7
|
|
8
8
|
module Journaled
|
9
9
|
SUPPORTED_QUEUE_ADAPTERS = %w(delayed delayed_job good_job que).freeze
|
@@ -20,7 +20,7 @@ module Journaled
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def enabled?
|
23
|
-
|
23
|
+
['0', 'false', false, 'f', ''].exclude?(ENV.fetch('JOURNALED_ENABLED', !development_or_test?))
|
24
24
|
end
|
25
25
|
|
26
26
|
def schema_providers
|
@@ -51,5 +51,17 @@ module Journaled
|
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
|
+
def self.tagged(**tags)
|
55
|
+
existing_tags = Current.tags
|
56
|
+
tag!(**tags)
|
57
|
+
yield
|
58
|
+
ensure
|
59
|
+
Current.tags = existing_tags
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.tag!(**tags)
|
63
|
+
Current.tags = Current.tags.merge(tags)
|
64
|
+
end
|
65
|
+
|
54
66
|
module_function :development_or_test?, :enabled?, :schema_providers, :commit_hash, :actor_uri, :detect_queue_adapter!
|
55
67
|
end
|
data/spec/dummy/config.ru
CHANGED
@@ -21,7 +21,7 @@ RSpec.describe Journaled::DeliveryJob do
|
|
21
21
|
end
|
22
22
|
|
23
23
|
it 'makes requests to AWS to put the event on the Kinesis with the correct body' do
|
24
|
-
event = described_class.perform_now(args)
|
24
|
+
event = described_class.perform_now(**args)
|
25
25
|
|
26
26
|
expect(event.shard_id).to eq '101'
|
27
27
|
expect(event.sequence_number).to eq '101123'
|
@@ -60,7 +60,7 @@ RSpec.describe Journaled::DeliveryJob do
|
|
60
60
|
end
|
61
61
|
|
62
62
|
it 'initializes a Kinesis client with assume role credentials' do
|
63
|
-
described_class.perform_now(args)
|
63
|
+
described_class.perform_now(**args)
|
64
64
|
|
65
65
|
expect(Aws::AssumeRoleCredentials).to have_received(:new).with(
|
66
66
|
client: aws_sts_client,
|
@@ -74,7 +74,7 @@ RSpec.describe Journaled::DeliveryJob do
|
|
74
74
|
let(:stream_name) { nil }
|
75
75
|
|
76
76
|
it 'raises an KeyError error' do
|
77
|
-
expect { described_class.perform_now(args) }.to raise_error ArgumentError, 'missing keyword: stream_name'
|
77
|
+
expect { described_class.perform_now(**args) }.to raise_error ArgumentError, 'missing keyword: stream_name'
|
78
78
|
end
|
79
79
|
end
|
80
80
|
|
@@ -99,7 +99,7 @@ RSpec.describe Journaled::DeliveryJob do
|
|
99
99
|
end
|
100
100
|
|
101
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)
|
102
|
+
event = described_class.perform_now(**args)
|
103
103
|
|
104
104
|
expect(event.shard_id).to eq '101'
|
105
105
|
expect(event.sequence_number).to eq '101123'
|
@@ -119,7 +119,7 @@ RSpec.describe Journaled::DeliveryJob do
|
|
119
119
|
end
|
120
120
|
|
121
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)
|
122
|
+
event = described_class.perform_now(**args)
|
123
123
|
|
124
124
|
expect(event.shard_id).to eq '101'
|
125
125
|
expect(event.sequence_number).to eq '101123'
|
@@ -138,7 +138,7 @@ RSpec.describe Journaled::DeliveryJob do
|
|
138
138
|
|
139
139
|
it 'catches the error and re-raises a subclass of NotTrulyExceptionalError and logs about the failure' do
|
140
140
|
allow(Rails.logger).to receive(:error)
|
141
|
-
expect { described_class.perform_now(args) }.to raise_error described_class::KinesisTemporaryFailure
|
141
|
+
expect { described_class.perform_now(**args) }.to raise_error described_class::KinesisTemporaryFailure
|
142
142
|
expect(Rails.logger).to have_received(:error).with(
|
143
143
|
"Kinesis Error - Server Error occurred - Aws::Kinesis::Errors::InternalFailure",
|
144
144
|
).once
|
@@ -152,7 +152,7 @@ RSpec.describe Journaled::DeliveryJob do
|
|
152
152
|
|
153
153
|
it 'catches the error and re-raises a subclass of NotTrulyExceptionalError and logs about the failure' do
|
154
154
|
allow(Rails.logger).to receive(:error)
|
155
|
-
expect { described_class.perform_now(args) }.to raise_error described_class::KinesisTemporaryFailure
|
155
|
+
expect { described_class.perform_now(**args) }.to raise_error described_class::KinesisTemporaryFailure
|
156
156
|
expect(Rails.logger).to have_received(:error).with(/\AKinesis Error/).once
|
157
157
|
end
|
158
158
|
end
|
@@ -163,7 +163,7 @@ RSpec.describe Journaled::DeliveryJob do
|
|
163
163
|
end
|
164
164
|
|
165
165
|
it 'raises an error that subclasses Aws::Kinesis::Errors::ServiceError' do
|
166
|
-
expect { described_class.perform_now(args) }.to raise_error Aws::Kinesis::Errors::ServiceError
|
166
|
+
expect { described_class.perform_now(**args) }.to raise_error Aws::Kinesis::Errors::ServiceError
|
167
167
|
end
|
168
168
|
end
|
169
169
|
|
@@ -173,7 +173,7 @@ RSpec.describe Journaled::DeliveryJob do
|
|
173
173
|
end
|
174
174
|
|
175
175
|
it 'raises an AccessDeniedException error' do
|
176
|
-
expect { described_class.perform_now(args) }.to raise_error Aws::Kinesis::Errors::AccessDeniedException
|
176
|
+
expect { described_class.perform_now(**args) }.to raise_error Aws::Kinesis::Errors::AccessDeniedException
|
177
177
|
end
|
178
178
|
end
|
179
179
|
|
@@ -184,7 +184,7 @@ RSpec.describe Journaled::DeliveryJob do
|
|
184
184
|
|
185
185
|
it 'catches the error and re-raises a subclass of NotTrulyExceptionalError and logs about the failure' do
|
186
186
|
allow(Rails.logger).to receive(:error)
|
187
|
-
expect { described_class.perform_now(args) }.to raise_error described_class::KinesisTemporaryFailure
|
187
|
+
expect { described_class.perform_now(**args) }.to raise_error described_class::KinesisTemporaryFailure
|
188
188
|
expect(Rails.logger).to have_received(:error).with(
|
189
189
|
"Kinesis Error - Networking Error occurred - Seahorse::Client::NetworkingError",
|
190
190
|
).once
|
data/spec/lib/journaled_spec.rb
CHANGED
@@ -75,12 +75,10 @@ RSpec.describe Journaled do
|
|
75
75
|
end
|
76
76
|
|
77
77
|
around do |example|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
ActiveJob::Base.enable_test_adapter(ActiveJob::QueueAdapters::TestAdapter.new)
|
83
|
-
end
|
78
|
+
example.run
|
79
|
+
ensure
|
80
|
+
ActiveJob::Base.queue_adapter = :test
|
81
|
+
ActiveJob::Base.enable_test_adapter(ActiveJob::QueueAdapters::TestAdapter.new)
|
84
82
|
end
|
85
83
|
|
86
84
|
it 'does not raise an error' do
|
@@ -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 be_nil
|
36
|
+
expect(Journaled::Current.actor).to be_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
|
@@ -44,7 +44,7 @@ RSpec.describe Journaled::Changes do
|
|
44
44
|
|
45
45
|
subject { klass.new }
|
46
46
|
|
47
|
-
let(:change_writer) {
|
47
|
+
let(:change_writer) { instance_double(Journaled::ChangeWriter, create: true, update: true, delete: true) }
|
48
48
|
|
49
49
|
before do
|
50
50
|
allow(Journaled::ChangeWriter).to receive(:new) do |opts|
|
@@ -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
|
@@ -139,7 +139,7 @@ RSpec.describe Journaled::ChangeWriter do
|
|
139
139
|
end
|
140
140
|
|
141
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
|
142
|
+
expect(subject.journaled_change_for("update", {}).journaled_stream_name).to be_nil
|
143
143
|
end
|
144
144
|
|
145
145
|
context "with journaled default app name set" do
|
@@ -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
|
@@ -8,7 +8,7 @@ RSpec.describe Journaled::JsonSchemaModel::Validator do
|
|
8
8
|
let(:attributes_to_validate) do
|
9
9
|
{
|
10
10
|
some_string: some_string_value,
|
11
|
-
some_decimal: 0.1
|
11
|
+
some_decimal: BigDecimal('0.1'),
|
12
12
|
some_time: Time.zone.parse('2017-01-20 15:16:17 -05:00'),
|
13
13
|
some_int: some_int_value,
|
14
14
|
some_optional_string: some_optional_string_value,
|
@@ -66,7 +66,7 @@ RSpec.describe Journaled::JsonSchemaModel::Validator do
|
|
66
66
|
context 'when all the required fields are provided' do
|
67
67
|
context 'when all the fields are provided' do
|
68
68
|
it 'is valid' do
|
69
|
-
expect(subject.validate!(json_to_validate)).to
|
69
|
+
expect(subject.validate!(json_to_validate)).to be true
|
70
70
|
end
|
71
71
|
end
|
72
72
|
|
@@ -74,7 +74,7 @@ RSpec.describe Journaled::JsonSchemaModel::Validator do
|
|
74
74
|
let(:attributes_to_validate) do
|
75
75
|
{
|
76
76
|
some_string: some_string_value,
|
77
|
-
some_decimal: 0.1
|
77
|
+
some_decimal: BigDecimal('0.1'),
|
78
78
|
some_time: Time.zone.parse('2017-01-20 15:16:17 -05:00'),
|
79
79
|
some_int: some_int_value,
|
80
80
|
some_nullable_field: some_nullable_field,
|
@@ -82,7 +82,7 @@ RSpec.describe Journaled::JsonSchemaModel::Validator do
|
|
82
82
|
end
|
83
83
|
|
84
84
|
it 'is valid' do
|
85
|
-
expect(subject.validate!(json_to_validate)).to
|
85
|
+
expect(subject.validate!(json_to_validate)).to be true
|
86
86
|
end
|
87
87
|
end
|
88
88
|
|
@@ -90,7 +90,7 @@ RSpec.describe Journaled::JsonSchemaModel::Validator do
|
|
90
90
|
let(:some_nullable_optional_field) { nil }
|
91
91
|
|
92
92
|
it 'is valid' do
|
93
|
-
expect(subject.validate!(json_to_validate)).to
|
93
|
+
expect(subject.validate!(json_to_validate)).to be true
|
94
94
|
end
|
95
95
|
end
|
96
96
|
|
@@ -107,7 +107,7 @@ RSpec.describe Journaled::JsonSchemaModel::Validator do
|
|
107
107
|
let(:attributes_to_validate) do
|
108
108
|
{
|
109
109
|
some_string: some_string_value,
|
110
|
-
some_decimal: 0.1
|
110
|
+
some_decimal: BigDecimal('0.1'),
|
111
111
|
some_time: Time.zone.parse('2017-01-20 15:16:17 -05:00'),
|
112
112
|
}
|
113
113
|
end
|
@@ -1,11 +1,12 @@
|
|
1
1
|
require 'rails_helper'
|
2
2
|
|
3
3
|
RSpec.describe Journaled::Writer do
|
4
|
+
let(:event_class) { Class.new { include Journaled::Event } }
|
4
5
|
subject { described_class.new journaled_event: journaled_event }
|
5
6
|
|
6
7
|
describe '#initialize' do
|
7
8
|
context 'when the Journaled Event does not implement all the necessary methods' do
|
8
|
-
let(:journaled_event) {
|
9
|
+
let(:journaled_event) { instance_double(event_class) }
|
9
10
|
|
10
11
|
it 'raises on initialization' do
|
11
12
|
expect { subject }.to raise_error RuntimeError, /An enqueued event must respond to/
|
@@ -14,7 +15,8 @@ RSpec.describe Journaled::Writer do
|
|
14
15
|
|
15
16
|
context 'when the Journaled Event returns non-present values for some of the required methods' do
|
16
17
|
let(:journaled_event) do
|
17
|
-
|
18
|
+
instance_double(
|
19
|
+
event_class,
|
18
20
|
journaled_schema_name: nil,
|
19
21
|
journaled_attributes: {},
|
20
22
|
journaled_partition_key: '',
|
@@ -30,7 +32,8 @@ RSpec.describe Journaled::Writer do
|
|
30
32
|
|
31
33
|
context 'when the Journaled Event complies with the API' do
|
32
34
|
let(:journaled_event) do
|
33
|
-
|
35
|
+
instance_double(
|
36
|
+
event_class,
|
34
37
|
journaled_schema_name: :fake_schema_name,
|
35
38
|
journaled_attributes: { foo: :bar },
|
36
39
|
journaled_partition_key: 'fake_partition_key',
|
@@ -71,12 +74,14 @@ RSpec.describe Journaled::Writer do
|
|
71
74
|
|
72
75
|
let(:journaled_enqueue_opts) { {} }
|
73
76
|
let(:journaled_event) do
|
74
|
-
|
75
|
-
|
77
|
+
instance_double(
|
78
|
+
event_class,
|
79
|
+
journaled_schema_name: 'fake_schema_name',
|
76
80
|
journaled_attributes: journaled_event_attributes,
|
77
81
|
journaled_partition_key: 'fake_partition_key',
|
78
82
|
journaled_stream_name: 'my_app_events',
|
79
83
|
journaled_enqueue_opts: journaled_enqueue_opts,
|
84
|
+
tagged?: false,
|
80
85
|
)
|
81
86
|
end
|
82
87
|
|
@@ -84,7 +89,9 @@ RSpec.describe Journaled::Writer do
|
|
84
89
|
let(:journaled_event_attributes) { { foo: 1 } }
|
85
90
|
|
86
91
|
it 'raises an error and does not enqueue anything' do
|
87
|
-
expect { subject.journal! }
|
92
|
+
expect { subject.journal! }
|
93
|
+
.to raise_error(JSON::Schema::ValidationError)
|
94
|
+
.and not_journal_event_including(anything)
|
88
95
|
expect(enqueued_jobs.count).to eq 0
|
89
96
|
end
|
90
97
|
end
|
@@ -94,7 +101,9 @@ RSpec.describe Journaled::Writer do
|
|
94
101
|
let(:journaled_event_attributes) { { id: 'FAKE_UUID', event_type: 'fake_event', created_at: Time.zone.now, foo: 1 } }
|
95
102
|
|
96
103
|
it 'raises an error and does not enqueue anything' do
|
97
|
-
expect { subject.journal! }
|
104
|
+
expect { subject.journal! }
|
105
|
+
.to raise_error(JSON::Schema::ValidationError)
|
106
|
+
.and not_journal_event_including(anything)
|
98
107
|
expect(enqueued_jobs.count).to eq 0
|
99
108
|
end
|
100
109
|
end
|
@@ -103,7 +112,13 @@ RSpec.describe Journaled::Writer do
|
|
103
112
|
let(:journaled_event_attributes) { { id: 'FAKE_UUID', event_type: 'fake_event', created_at: Time.zone.now, foo: :bar } }
|
104
113
|
|
105
114
|
it 'creates a delivery with the app name passed through' do
|
106
|
-
expect { subject.journal! }
|
115
|
+
expect { subject.journal! }
|
116
|
+
.to change { enqueued_jobs.count }.from(0).to(1)
|
117
|
+
.and journal_event_including(journaled_event_attributes)
|
118
|
+
.with_schema_name('fake_schema_name')
|
119
|
+
.with_partition_key('fake_partition_key')
|
120
|
+
.with_stream_name('my_app_events')
|
121
|
+
.with_enqueue_opts({})
|
107
122
|
expect(enqueued_jobs.first[:args].first).to include('stream_name' => 'my_app_events')
|
108
123
|
end
|
109
124
|
|
@@ -118,11 +133,18 @@ RSpec.describe Journaled::Writer do
|
|
118
133
|
it 'defaults to the global default' do
|
119
134
|
expect { subject.journal! }.to change {
|
120
135
|
if Rails::VERSION::MAJOR < 6
|
121
|
-
enqueued_jobs.
|
136
|
+
enqueued_jobs.count { |j| j[:job] == Journaled::DeliveryJob }
|
122
137
|
else
|
123
|
-
enqueued_jobs.
|
138
|
+
enqueued_jobs.count { |j| j['job_class'] == 'Journaled::DeliveryJob' && j['priority'] == 999 }
|
124
139
|
end
|
125
140
|
}.from(0).to(1)
|
141
|
+
.and journal_event_including(journaled_event_attributes)
|
142
|
+
.with_schema_name('fake_schema_name')
|
143
|
+
.with_partition_key('fake_partition_key')
|
144
|
+
.with_stream_name('my_app_events')
|
145
|
+
.with_priority(999)
|
146
|
+
.and not_journal_event_including(anything)
|
147
|
+
.with_enqueue_opts(priority: 999) # with_enqueue_opts looks at event itself
|
126
148
|
end
|
127
149
|
end
|
128
150
|
|
@@ -132,14 +154,59 @@ RSpec.describe Journaled::Writer do
|
|
132
154
|
it 'enqueues a Journaled::DeliveryJob with the given priority' do
|
133
155
|
expect { subject.journal! }.to change {
|
134
156
|
if Rails::VERSION::MAJOR < 6
|
135
|
-
enqueued_jobs.
|
157
|
+
enqueued_jobs.count { |j| j[:job] == Journaled::DeliveryJob }
|
136
158
|
else
|
137
|
-
enqueued_jobs.
|
159
|
+
enqueued_jobs.count { |j| j['job_class'] == 'Journaled::DeliveryJob' && j['priority'] == 13 }
|
138
160
|
end
|
139
161
|
}.from(0).to(1)
|
162
|
+
.and journal_event_including(journaled_event_attributes)
|
163
|
+
.with_schema_name('fake_schema_name')
|
164
|
+
.with_partition_key('fake_partition_key')
|
165
|
+
.with_stream_name('my_app_events')
|
166
|
+
.with_priority(13)
|
167
|
+
.with_enqueue_opts(priority: 13)
|
140
168
|
end
|
141
169
|
end
|
142
170
|
end
|
143
171
|
end
|
172
|
+
|
173
|
+
context 'when the event is tagged' do
|
174
|
+
before do
|
175
|
+
allow(journaled_event).to receive(:tagged?).and_return(true)
|
176
|
+
end
|
177
|
+
|
178
|
+
context 'and the "tags" attribute is not present' do
|
179
|
+
let(:journaled_event_attributes) do
|
180
|
+
{ id: 'FAKE_UUID', event_type: 'fake_event', created_at: Time.zone.now, foo: 'bar' }
|
181
|
+
end
|
182
|
+
|
183
|
+
it 'raises an error and does not enqueue anything' do
|
184
|
+
expect { subject.journal! }
|
185
|
+
.to not_journal_events_including(anything)
|
186
|
+
.and raise_error JSON::Schema::ValidationError
|
187
|
+
expect(enqueued_jobs.count).to eq 0
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
context 'and the "tags" attribute is present' do
|
192
|
+
let(:journaled_event_attributes) do
|
193
|
+
{ id: 'FAKE_UUID', event_type: 'fake_event', created_at: Time.zone.now, foo: 'bar', tags: { baz: 'bat' } }
|
194
|
+
end
|
195
|
+
|
196
|
+
it 'creates a delivery with the app name passed through' do
|
197
|
+
expect { subject.journal! }
|
198
|
+
.to change { enqueued_jobs.count }.from(0).to(1)
|
199
|
+
.and journal_event_including(journaled_event_attributes)
|
200
|
+
.with_schema_name('fake_schema_name')
|
201
|
+
.with_partition_key('fake_partition_key')
|
202
|
+
.with_stream_name('my_app_events')
|
203
|
+
.with_enqueue_opts({})
|
204
|
+
.with_priority(Journaled.job_priority)
|
205
|
+
.and not_journal_event_including(anything)
|
206
|
+
.with_enqueue_opts(priority: Journaled.job_priority)
|
207
|
+
expect(enqueued_jobs.first[:args].first).to include('stream_name' => 'my_app_events')
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
144
211
|
end
|
145
212
|
end
|
data/spec/rails_helper.rb
CHANGED
@@ -7,7 +7,7 @@ require 'timecop'
|
|
7
7
|
require 'webmock/rspec'
|
8
8
|
require 'journaled/rspec'
|
9
9
|
|
10
|
-
Dir[Rails.root.join('
|
10
|
+
Dir[Rails.root.join('../support/**/*.rb')].sort.each { |f| require f }
|
11
11
|
|
12
12
|
RSpec.configure do |config|
|
13
13
|
config.use_transactional_fixtures = true
|