journaled 2.0.0.alpha1 → 2.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +95 -25
- data/app/models/concerns/journaled/changes.rb +41 -0
- data/app/models/journaled/actor_uri_provider.rb +22 -0
- data/app/models/journaled/change_definition.rb +17 -1
- data/app/models/journaled/change_writer.rb +3 -22
- data/app/models/journaled/event.rb +0 -4
- data/config/initializers/change_protection.rb +3 -0
- data/lib/journaled.rb +9 -1
- data/lib/journaled/relation_change_protection.rb +27 -0
- data/lib/journaled/rspec.rb +18 -0
- data/lib/journaled/version.rb +1 -1
- data/spec/dummy/README.rdoc +28 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +26 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/database.yml +21 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +37 -0
- data/spec/dummy/config/environments/production.rb +78 -0
- data/spec/dummy/config/environments/test.rb +39 -0
- data/spec/dummy/config/initializers/assets.rb +8 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +4 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +56 -0
- data/spec/dummy/config/secrets.yml +22 -0
- data/spec/dummy/db/migrate/20180606205114_create_delayed_jobs.rb +18 -0
- data/spec/dummy/db/schema.rb +31 -0
- data/spec/dummy/log/development.log +34 -0
- data/spec/dummy/log/test.log +34540 -0
- data/spec/dummy/public/404.html +67 -0
- data/spec/dummy/public/422.html +67 -0
- data/spec/dummy/public/500.html +66 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/lib/journaled_spec.rb +51 -0
- data/spec/models/concerns/journaled/actor_spec.rb +46 -0
- data/spec/models/concerns/journaled/changes_spec.rb +94 -0
- data/spec/models/database_change_protection_spec.rb +106 -0
- data/spec/models/journaled/actor_uri_provider_spec.rb +41 -0
- data/spec/models/journaled/change_writer_spec.rb +276 -0
- data/spec/models/journaled/delivery_spec.rb +156 -0
- data/spec/models/journaled/event_spec.rb +145 -0
- data/spec/models/journaled/json_schema_model/validator_spec.rb +133 -0
- data/spec/models/journaled/writer_spec.rb +129 -0
- data/spec/rails_helper.rb +20 -0
- data/spec/spec_helper.rb +22 -0
- data/spec/support/delayed_job_spec_helper.rb +11 -0
- data/spec/support/environment_spec_helper.rb +16 -0
- metadata +113 -4
@@ -0,0 +1,145 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
RSpec.describe Journaled::Event do
|
4
|
+
let(:sample_journaled_event_class) do
|
5
|
+
SomeClassName = Class.new do
|
6
|
+
include Journaled::Event
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
after do
|
11
|
+
Object.send(:remove_const, :SomeClassName) if defined?(SomeClassName)
|
12
|
+
Object.send(:remove_const, :SomeModule) if defined?(SomeModule)
|
13
|
+
end
|
14
|
+
|
15
|
+
let(:sample_journaled_event) { sample_journaled_event_class.new }
|
16
|
+
|
17
|
+
describe '#journal!' do
|
18
|
+
let(:mock_journaled_writer) { instance_double(Journaled::Writer, journal!: nil) }
|
19
|
+
|
20
|
+
before do
|
21
|
+
allow(Journaled::Writer).to receive(:new).and_return(mock_journaled_writer)
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'creates a Journaled::Writer with this event and journals it' do
|
25
|
+
sample_journaled_event.journal!
|
26
|
+
expect(Journaled::Writer).to have_received(:new).with(journaled_event: sample_journaled_event)
|
27
|
+
expect(mock_journaled_writer).to have_received(:journal!)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe '#journaled_schema_name' do
|
32
|
+
it 'returns the underscored version on the class name' do
|
33
|
+
expect(sample_journaled_event.journaled_schema_name).to eq 'some_class_name'
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'when the class is modularized' do
|
37
|
+
let(:sample_journaled_event_class) do
|
38
|
+
SomeModule = Module.new
|
39
|
+
SomeModule::SomeClassName = Class.new do
|
40
|
+
include Journaled::Event
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'returns the underscored version on the class name' do
|
45
|
+
expect(sample_journaled_event.journaled_schema_name).to eq 'some_module/some_class_name'
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe '#event_type' do
|
51
|
+
it 'returns the underscored version on the class name' do
|
52
|
+
expect(sample_journaled_event.event_type).to eq 'some_class_name'
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'when the class is modularized' do
|
56
|
+
let(:sample_journaled_event_class) do
|
57
|
+
SomeModule = Module.new
|
58
|
+
SomeModule::SomeClassName = Class.new do
|
59
|
+
include Journaled::Event
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'returns the underscored version on the class name, with slashes replaced with underscores' do
|
64
|
+
expect(sample_journaled_event.event_type).to eq 'some_module_some_class_name'
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe '#journaled_partition_key' do
|
70
|
+
it 'returns the #event_type' do
|
71
|
+
expect(sample_journaled_event.journaled_partition_key).to eq 'some_class_name'
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe '#journaled_app_name' do
|
76
|
+
it 'returns nil in the base class so it can be set explicitly in apps spanning multiple app domains' do
|
77
|
+
expect(sample_journaled_event.journaled_app_name).to be_nil
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'returns the journaled default if set' do
|
81
|
+
allow(Journaled).to receive(:default_app_name).and_return("my_app")
|
82
|
+
expect(sample_journaled_event.journaled_app_name).to eq("my_app")
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe '#journaled_attributes' do
|
87
|
+
let(:fake_uuid) { 'FAKE_UUID' }
|
88
|
+
let(:frozen_time) { Time.zone.parse('15/2/2017 13:00') }
|
89
|
+
before do
|
90
|
+
allow(SecureRandom).to receive(:uuid).and_return(fake_uuid).once
|
91
|
+
end
|
92
|
+
around do |example|
|
93
|
+
Timecop.freeze(frozen_time) { example.run }
|
94
|
+
end
|
95
|
+
|
96
|
+
context 'when no additional attributes have been defined' do
|
97
|
+
it 'returns the base attributes, and memoizes them after the first call' do
|
98
|
+
expect(sample_journaled_event.journaled_attributes).to eq id: fake_uuid, created_at: frozen_time, event_type: 'some_class_name'
|
99
|
+
expect(sample_journaled_event.journaled_attributes).to eq id: fake_uuid, created_at: frozen_time, event_type: 'some_class_name'
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
context 'when there are additional attributes specified, but not defined' do
|
104
|
+
let(:sample_journaled_event_class) do
|
105
|
+
SomeClassName = Class.new do
|
106
|
+
include Journaled::Event
|
107
|
+
|
108
|
+
journal_attributes :foo
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'raises a no method error' do
|
113
|
+
expect { sample_journaled_event.journaled_attributes }.to raise_error NoMethodError
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
context 'when there are additional attributes specified and defined' do
|
118
|
+
let(:sample_journaled_event_class) do
|
119
|
+
SomeClassName = Class.new do
|
120
|
+
include Journaled::Event
|
121
|
+
|
122
|
+
journal_attributes :foo, :bar
|
123
|
+
|
124
|
+
def foo
|
125
|
+
'foo_return'
|
126
|
+
end
|
127
|
+
|
128
|
+
def bar
|
129
|
+
'bar_return'
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'returns the specified attributes plus the base ones' do
|
135
|
+
expect(sample_journaled_event.journaled_attributes).to eq(
|
136
|
+
id: fake_uuid,
|
137
|
+
created_at: frozen_time,
|
138
|
+
event_type: 'some_class_name',
|
139
|
+
foo: 'foo_return',
|
140
|
+
bar: 'bar_return'
|
141
|
+
)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
RSpec.describe Journaled::JsonSchemaModel::Validator do
|
4
|
+
subject { described_class.new schema_name }
|
5
|
+
|
6
|
+
describe '#validate!' do
|
7
|
+
let(:json_to_validate) { attributes_to_validate.to_json }
|
8
|
+
let(:attributes_to_validate) do
|
9
|
+
{
|
10
|
+
some_string: some_string_value,
|
11
|
+
some_decimal: 0.1.to_d,
|
12
|
+
some_time: Time.zone.parse('2017-01-20 15:16:17 -05:00'),
|
13
|
+
some_int: some_int_value,
|
14
|
+
some_optional_string: some_optional_string_value,
|
15
|
+
some_nullable_field: some_nullable_field
|
16
|
+
}
|
17
|
+
end
|
18
|
+
let(:some_int_value) { 3 }
|
19
|
+
let(:some_string_value) { 'SOME_STRING' }
|
20
|
+
let(:some_optional_string_value) { 'SOME_OPTIONAL_STRING' }
|
21
|
+
let(:some_nullable_field) { 'VALUE' }
|
22
|
+
|
23
|
+
subject { described_class.new schema_name }
|
24
|
+
|
25
|
+
context 'when the schema name matches a schema in journaled' do
|
26
|
+
let(:schema_name) { :fake_json_schema_name }
|
27
|
+
let(:gem_path) { Journaled::Engine.root.join "journaled_schemas/#{schema_name}.json" }
|
28
|
+
let(:schema_path) { Rails.root.join "journaled_schemas", "#{schema_name}.json" }
|
29
|
+
let(:schema_file_contents) do
|
30
|
+
<<-JSON
|
31
|
+
{
|
32
|
+
"title": "Person",
|
33
|
+
"type": "object",
|
34
|
+
"properties": {
|
35
|
+
"some_string": {
|
36
|
+
"type": "string"
|
37
|
+
},
|
38
|
+
"some_decimal": {
|
39
|
+
"type": "string"
|
40
|
+
},
|
41
|
+
"some_time": {
|
42
|
+
"type": "string"
|
43
|
+
},
|
44
|
+
"some_int": {
|
45
|
+
"type": "integer"
|
46
|
+
},
|
47
|
+
"some_optional_string": {
|
48
|
+
"type": "string"
|
49
|
+
},
|
50
|
+
"some_nullable_field": {
|
51
|
+
"type": ["string", "null"]
|
52
|
+
}
|
53
|
+
},
|
54
|
+
"required": ["some_string", "some_decimal", "some_time", "some_int", "some_nullable_field"]
|
55
|
+
}
|
56
|
+
JSON
|
57
|
+
end
|
58
|
+
|
59
|
+
before do
|
60
|
+
allow(File).to receive(:exist?).and_call_original
|
61
|
+
allow(File).to receive(:exist?).with(gem_path).and_return(false)
|
62
|
+
allow(File).to receive(:exist?).with(schema_path).and_return(true)
|
63
|
+
allow(File).to receive(:read).with(schema_path).and_return(schema_file_contents)
|
64
|
+
end
|
65
|
+
|
66
|
+
context 'when all the required fields are provided' do
|
67
|
+
context 'when all the fields are provided' do
|
68
|
+
it 'is valid' do
|
69
|
+
expect(subject.validate!(json_to_validate)).to eq true
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
context 'when an optional field is missing' do
|
74
|
+
let(:attributes_to_validate) do
|
75
|
+
{
|
76
|
+
some_string: some_string_value,
|
77
|
+
some_decimal: 0.1.to_d,
|
78
|
+
some_time: Time.zone.parse('2017-01-20 15:16:17 -05:00'),
|
79
|
+
some_int: some_int_value,
|
80
|
+
some_nullable_field: some_nullable_field
|
81
|
+
}
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'is valid' do
|
85
|
+
expect(subject.validate!(json_to_validate)).to eq true
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
context 'when a nullable field is nil' do
|
90
|
+
let(:some_nullable_optional_field) { nil }
|
91
|
+
|
92
|
+
it 'is valid' do
|
93
|
+
expect(subject.validate!(json_to_validate)).to eq true
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
context 'but one of the required fields is of the wrong type' do
|
98
|
+
let(:some_int_value) { 'NOT_AN_INTEGER' }
|
99
|
+
|
100
|
+
it 'is NOT valid' do
|
101
|
+
expect { subject.validate! json_to_validate }.to raise_error JSON::Schema::ValidationError
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
context 'when not all the required fields are provided' do
|
107
|
+
let(:attributes_to_validate) do
|
108
|
+
{
|
109
|
+
some_string: some_string_value,
|
110
|
+
some_decimal: 0.1.to_d,
|
111
|
+
some_time: Time.zone.parse('2017-01-20 15:16:17 -05:00')
|
112
|
+
}
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'is NOT valid' do
|
116
|
+
expect { subject.validate! json_to_validate }.to raise_error JSON::Schema::ValidationError
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
context 'when the schema name does not match a schema in journaled' do
|
122
|
+
let(:schema_name) { :nonexistent_avro_scehma }
|
123
|
+
|
124
|
+
before do
|
125
|
+
allow(File).to receive(:exist?).and_return(false)
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'raises an error loading the schema' do
|
129
|
+
expect { subject.validate! json_to_validate }.to raise_error(/not found in any of Journaled::Engine.root,/)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
RSpec.describe Journaled::Writer do
|
4
|
+
subject { described_class.new journaled_event: journaled_event }
|
5
|
+
|
6
|
+
describe '#initialize' do
|
7
|
+
context 'when the Journaled Event does not implement all the necessary methods' do
|
8
|
+
let(:journaled_event) { double }
|
9
|
+
|
10
|
+
it 'raises on initialization' do
|
11
|
+
expect { subject }.to raise_error RuntimeError, /An enqueued event must respond to/
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
context 'when the Journaled Event returns non-present values for some of the required methods' do
|
16
|
+
let(:journaled_event) do
|
17
|
+
double(
|
18
|
+
journaled_schema_name: nil,
|
19
|
+
journaled_attributes: {},
|
20
|
+
journaled_partition_key: '',
|
21
|
+
journaled_app_name: nil
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'raises on initialization' do
|
26
|
+
expect { subject }.to raise_error RuntimeError, /An enqueued event must have a non-nil response to/
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'when the Journaled Event complies with the API' do
|
31
|
+
let(:journaled_event) do
|
32
|
+
double(
|
33
|
+
journaled_schema_name: :fake_schema_name,
|
34
|
+
journaled_attributes: { foo: :bar },
|
35
|
+
journaled_partition_key: 'fake_partition_key',
|
36
|
+
journaled_app_name: nil
|
37
|
+
)
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'does not raise on initialization' do
|
41
|
+
expect { subject }.to_not raise_error
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe '#journal!' do
|
47
|
+
let(:schema_path) { Journaled::Engine.root.join "journaled_schemas/fake_schema_name.json" }
|
48
|
+
let(:schema_file_contents) do
|
49
|
+
<<-JSON
|
50
|
+
{
|
51
|
+
"title": "Foo",
|
52
|
+
"type": "object",
|
53
|
+
"properties": {
|
54
|
+
"foo": {
|
55
|
+
"type": "string"
|
56
|
+
}
|
57
|
+
},
|
58
|
+
"required": ["foo"]
|
59
|
+
}
|
60
|
+
JSON
|
61
|
+
end
|
62
|
+
|
63
|
+
before do
|
64
|
+
allow(File).to receive(:exist?).and_call_original
|
65
|
+
allow(File).to receive(:exist?).with(schema_path).and_return(true)
|
66
|
+
allow(File).to receive(:read).and_call_original
|
67
|
+
allow(File).to receive(:read).with(schema_path).and_return(schema_file_contents)
|
68
|
+
end
|
69
|
+
|
70
|
+
let(:journaled_event) do
|
71
|
+
double(
|
72
|
+
journaled_schema_name: :fake_schema_name,
|
73
|
+
journaled_attributes: journaled_event_attributes,
|
74
|
+
journaled_partition_key: 'fake_partition_key',
|
75
|
+
journaled_app_name: 'my_app'
|
76
|
+
)
|
77
|
+
end
|
78
|
+
|
79
|
+
around do |example|
|
80
|
+
with_jobs_delayed { example.run }
|
81
|
+
end
|
82
|
+
|
83
|
+
context 'when the journaled event does NOT comply with the base_event schema' do
|
84
|
+
let(:journaled_event_attributes) { { foo: 1 } }
|
85
|
+
|
86
|
+
it 'raises an error and does not enqueue anything' do
|
87
|
+
expect { subject.journal! }.to raise_error JSON::Schema::ValidationError
|
88
|
+
expect(Delayed::Job.where('handler like ?', '%Journaled::Delivery%').count).to eq 0
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
context 'when the event complies with the base_event schema' do
|
93
|
+
context 'when the specific json schema is NOT valid' do
|
94
|
+
let(:journaled_event_attributes) { { id: 'FAKE_UUID', event_type: 'fake_event', created_at: Time.zone.now, foo: 1 } }
|
95
|
+
|
96
|
+
it 'raises an error and does not enqueue anything' do
|
97
|
+
expect { subject.journal! }.to raise_error JSON::Schema::ValidationError
|
98
|
+
expect(Delayed::Job.where('handler like ?', '%Journaled::Delivery%').count).to eq 0
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
context 'when the specific json schema is also valid' do
|
103
|
+
let(:journaled_event_attributes) { { id: 'FAKE_UUID', event_type: 'fake_event', created_at: Time.zone.now, foo: :bar } }
|
104
|
+
|
105
|
+
it 'creates a delivery with the app name passed through' do
|
106
|
+
allow(Journaled::Delivery).to receive(:new).and_call_original
|
107
|
+
subject.journal!
|
108
|
+
expect(Journaled::Delivery).to have_received(:new).with(hash_including(app_name: 'my_app'))
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'enqueues a Journaled::Delivery object with the serialized journaled_event at the lowest priority' do
|
112
|
+
expect { subject.journal! }.to change {
|
113
|
+
Delayed::Job.where('handler like ?', '%Journaled::Delivery%').where(priority: Journaled::JobPriority::EVENTUAL).count
|
114
|
+
}.from(0).to(1)
|
115
|
+
end
|
116
|
+
|
117
|
+
context 'when the Writer was initialized with a priority' do
|
118
|
+
subject { described_class.new journaled_event: journaled_event, priority: Journaled::JobPriority::INTERACTIVE }
|
119
|
+
|
120
|
+
it 'enqueues the event at the given priority' do
|
121
|
+
expect { subject.journal! }.to change {
|
122
|
+
Delayed::Job.where('handler like ?', '%Journaled::Delivery%').where(priority: Journaled::JobPriority::INTERACTIVE).count
|
123
|
+
}.from(0).to(1)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# This file is copied to spec/ when you run 'rails generate rspec:install'
|
2
|
+
ENV['RAILS_ENV'] ||= 'test'
|
3
|
+
require 'spec_helper'
|
4
|
+
require File.expand_path('../spec/dummy/config/environment', __dir__)
|
5
|
+
require 'rspec/rails'
|
6
|
+
require 'timecop'
|
7
|
+
require 'webmock/rspec'
|
8
|
+
require 'journaled/rspec'
|
9
|
+
require 'pry-rails'
|
10
|
+
|
11
|
+
Dir[Rails.root.join('..', 'support', '**', '*.rb')].each { |f| require f }
|
12
|
+
|
13
|
+
RSpec.configure do |config|
|
14
|
+
config.use_transactional_fixtures = true
|
15
|
+
|
16
|
+
config.infer_spec_type_from_file_location!
|
17
|
+
|
18
|
+
config.include DelayedJobSpecHelper
|
19
|
+
config.include EnvironmentSpecHelper
|
20
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'delayed_job_active_record'
|
2
|
+
rails_env = ENV['RAILS_ENV'] ||= 'test'
|
3
|
+
db_adapter = ENV['DB_ADAPTER'] ||= 'postgresql'
|
4
|
+
require File.expand_path('dummy/config/environment.rb', __dir__)
|
5
|
+
|
6
|
+
Rails.configuration.database_configuration[db_adapter][rails_env].tap do |c|
|
7
|
+
ActiveRecord::Tasks::DatabaseTasks.create(c)
|
8
|
+
ActiveRecord::Base.establish_connection(c)
|
9
|
+
load File.expand_path('dummy/db/schema.rb', __dir__)
|
10
|
+
end
|
11
|
+
|
12
|
+
RSpec.configure do |config|
|
13
|
+
config.expect_with :rspec do |expectations|
|
14
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
15
|
+
end
|
16
|
+
|
17
|
+
config.mock_with :rspec do |mocks|
|
18
|
+
mocks.verify_partial_doubles = true
|
19
|
+
end
|
20
|
+
|
21
|
+
config.order = :random
|
22
|
+
end
|