signinable 2.0.11 → 2.0.14
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/Rakefile +6 -4
- data/app/models/signin.rb +25 -4
- data/config/routes.rb +2 -0
- data/db/migrate/20140103165607_create_signins.rb +7 -5
- data/db/migrate/20180530131006_add_custom_data_to_sigins.rb +5 -5
- data/lib/signinable/engine.rb +6 -9
- data/lib/signinable/model_additions.rb +121 -51
- data/lib/signinable/version.rb +3 -1
- data/lib/signinable.rb +3 -1
- data/spec/dummy/Rakefile +3 -1
- data/spec/dummy/app/models/user.rb +3 -1
- data/spec/dummy/bin/bundle +3 -1
- data/spec/dummy/bin/rails +3 -1
- data/spec/dummy/bin/rake +2 -0
- data/spec/dummy/config/application.rb +5 -20
- data/spec/dummy/config/boot.rb +5 -3
- data/spec/dummy/config/environment.rb +3 -1
- data/spec/dummy/config/environments/development.rb +4 -2
- data/spec/dummy/config/environments/production.rb +2 -0
- data/spec/dummy/config/environments/test.rb +4 -2
- data/spec/dummy/config/initializers/backtrace_silencers.rb +1 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +2 -0
- data/spec/dummy/config/initializers/inflections.rb +1 -0
- data/spec/dummy/config/initializers/mime_types.rb +1 -0
- data/spec/dummy/config/initializers/secret_token.rb +2 -0
- data/spec/dummy/config/initializers/session_store.rb +2 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +2 -0
- data/spec/dummy/config/routes.rb +2 -0
- data/spec/dummy/config.ru +3 -1
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/migrate/20140103165606_create_users.rb +3 -1
- data/spec/dummy/db/schema.rb +23 -24
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/development.log +427 -0
- data/spec/dummy/log/test.log +20422 -0
- data/spec/factories/signins.rb +8 -0
- data/spec/factories/users.rb +7 -0
- data/spec/models/signin_spec.rb +85 -33
- data/spec/models/user_spec.rb +213 -84
- data/spec/rails_helper.rb +20 -0
- data/spec/spec_helper.rb +11 -12
- data/spec/support/utilities.rb +3 -2
- metadata +53 -14
- data/spec/factories/signin.rb +0 -8
- data/spec/factories/user.rb +0 -7
data/spec/models/signin_spec.rb
CHANGED
@@ -1,50 +1,102 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rails_helper'
|
2
4
|
|
3
5
|
describe Signin do
|
4
|
-
|
5
|
-
|
6
|
-
signin
|
7
|
-
end
|
6
|
+
describe '#expired?' do
|
7
|
+
let(:expiration_time) { Time.zone.now + 1.hour }
|
8
|
+
let(:signin) { create(:signin, expiration_time: expiration_time) }
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
signin = FactoryGirl.create(:signin)
|
12
|
-
signin.token = nil
|
13
|
-
signin.should_not be_valid
|
10
|
+
it 'returns false when not expired' do
|
11
|
+
expect(signin).to_not be_expired
|
14
12
|
end
|
15
13
|
|
16
|
-
it
|
17
|
-
|
14
|
+
it 'returns true when expired' do
|
15
|
+
Timecop.travel(expiration_time) do
|
16
|
+
expect(signin).to be_expired
|
17
|
+
end
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
21
|
+
describe '#expireable?' do
|
22
|
+
it 'returns false when expireable' do
|
23
|
+
signin = create(:signin, expiration_time: nil)
|
24
|
+
expect(signin).to_not be_expireable
|
25
|
+
end
|
25
26
|
|
26
|
-
|
27
|
-
|
28
|
-
|
27
|
+
it 'returns true when expireable' do
|
28
|
+
signin = create(:signin, expiration_time: Time.zone.now)
|
29
|
+
expect(signin).to be_expireable
|
29
30
|
end
|
30
31
|
end
|
31
32
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
33
|
+
describe '#expire!' do
|
34
|
+
it 'sets expiration_time to now' do
|
35
|
+
signin = create(:signin, expiration_time: (Time.zone.now + 1.hour))
|
36
|
+
allow(signin).to receive(:renew!)
|
37
|
+
signin.expire!
|
38
|
+
expect(signin).to have_received(:renew!).with(period: 0, ip: signin.ip, user_agent: signin.user_agent)
|
39
|
+
end
|
39
40
|
end
|
40
41
|
|
41
|
-
describe
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
42
|
+
describe '#renew!' do
|
43
|
+
let(:signin) { create(:signin) }
|
44
|
+
let(:attrs) do
|
45
|
+
{
|
46
|
+
period: 100,
|
47
|
+
ip: signin.ip,
|
48
|
+
user_agent: signin.user_agent,
|
49
|
+
refresh_token: false
|
50
|
+
}
|
51
|
+
end
|
52
|
+
|
53
|
+
before(:each) do
|
54
|
+
allow(signin).to receive(:update!)
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'updates ip and user_agent' do
|
58
|
+
signin.renew!(**attrs)
|
59
|
+
expect(signin).to have_received(:update!).with(hash_including(ip: signin.ip, user_agent: signin.user_agent))
|
60
|
+
end
|
61
|
+
|
62
|
+
context 'when expireable' do
|
63
|
+
before(:each) do
|
64
|
+
allow(signin).to receive(:expireable?).and_return(true)
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'updates expiration_time' do
|
68
|
+
Timecop.freeze do
|
69
|
+
signin.renew!(**attrs)
|
70
|
+
expect(signin).to have_received(:update!).with(hash_including(expiration_time: Time.zone.now + attrs[:period]))
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
context 'when not expireable' do
|
76
|
+
before(:each) do
|
77
|
+
allow(signin).to receive(:expireable?).and_return(false)
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'does not update expiration_time' do
|
81
|
+
signin.renew!(**attrs)
|
82
|
+
expect(signin).to have_received(:update!).with(hash_excluding(expiration_time: Time.zone.now + attrs[:period]))
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
context 'when need to refresh_token' do
|
87
|
+
it 'updates expiration_time' do
|
88
|
+
allow(SecureRandom).to receive(:urlsafe_base64).and_return('bla')
|
89
|
+
signin.renew!(**attrs.merge(refresh_token: true))
|
90
|
+
expect(signin).to have_received(:update!).with(hash_including(token: 'bla'))
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
context 'when no need to refresh_token' do
|
95
|
+
it 'does not update expiration_time' do
|
96
|
+
allow(SecureRandom).to receive(:urlsafe_base64).and_return('bla')
|
97
|
+
signin.renew!(**attrs)
|
98
|
+
expect(signin).to have_received(:update!).with(hash_excluding(token: 'bla'))
|
99
|
+
end
|
48
100
|
end
|
49
101
|
end
|
50
102
|
end
|
data/spec/models/user_spec.rb
CHANGED
@@ -1,133 +1,262 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rails_helper'
|
2
4
|
|
3
5
|
describe User do
|
6
|
+
let(:credentials) { ['127.0.0.1', 'user_agent'] }
|
7
|
+
let(:other_credentials) { ['127.0.0.2', 'user_agent2'] }
|
8
|
+
let(:user) { create(:user) }
|
9
|
+
|
4
10
|
before :each do
|
5
11
|
Timecop.freeze
|
6
|
-
User.signin_expiration = 2.hours
|
7
|
-
User.signin_simultaneous = true
|
8
|
-
User.signin_restrictions = []
|
9
|
-
@user = FactoryGirl.create(:user)
|
10
|
-
@credentials = ['127.0.0.1', 'user_agent']
|
11
|
-
@other_credentials = ['127.0.0.2', 'user_agent2']
|
12
12
|
end
|
13
13
|
|
14
14
|
after :each do
|
15
15
|
Timecop.return
|
16
16
|
end
|
17
17
|
|
18
|
-
describe
|
19
|
-
it
|
20
|
-
expect
|
21
|
-
sign_in_user(
|
22
|
-
|
18
|
+
describe '#signin' do
|
19
|
+
it 'should create Signin' do
|
20
|
+
expect do
|
21
|
+
sign_in_user(user, credentials)
|
22
|
+
end.to change(Signin, :count).by(1)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'sets jwt on user instance' do
|
26
|
+
expect do
|
27
|
+
sign_in_user(user, credentials)
|
28
|
+
end.to change(user, :jwt).from(nil)
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should generate jwt with correct payload' do
|
32
|
+
sign_in_user(user, credentials)
|
33
|
+
signin = user.last_signin
|
34
|
+
payload = JWT.decode(user.jwt, 'test', true, { algorithm: 'HS256' })[0]
|
35
|
+
expect(payload).to include(
|
36
|
+
'refresh_token' => signin.token,
|
37
|
+
'signinable_id' => user.id
|
38
|
+
)
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'should set expiration_time' do
|
42
|
+
sign_in_user(user, credentials)
|
43
|
+
signin = user.last_signin
|
44
|
+
expect(signin.expiration_time.to_i).to eq((Time.zone.now + User.refresh_exp).to_i)
|
23
45
|
end
|
24
46
|
|
25
|
-
it
|
26
|
-
|
27
|
-
|
47
|
+
it 'should not set expiration_time' do
|
48
|
+
allow(described_class).to receive(:refresh_exp).and_return(0)
|
49
|
+
sign_in_user(user, credentials)
|
50
|
+
signin = user.last_signin
|
51
|
+
expect(signin.expiration_time).to be_nil
|
28
52
|
end
|
29
53
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
54
|
+
context 'when simultaneous signins enabled' do
|
55
|
+
before do
|
56
|
+
allow(described_class).to receive(:simultaneous_signings).and_return(true)
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'does not expire active signins' do
|
60
|
+
sign_in_user(user, credentials)
|
61
|
+
sign_in_user(user, credentials)
|
62
|
+
expect(user.signins.active.count).to eq(2)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context 'when simultaneous signins disabled' do
|
67
|
+
before do
|
68
|
+
allow(described_class).to receive(:simultaneous_signings).and_return(false)
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'expires active signins' do
|
72
|
+
sign_in_user(user, credentials)
|
73
|
+
sign_in_user(user, credentials)
|
74
|
+
expect(user.signins.active.count).to eq(1)
|
75
|
+
end
|
34
76
|
end
|
35
77
|
end
|
36
78
|
|
37
|
-
describe
|
38
|
-
it
|
39
|
-
|
40
|
-
|
41
|
-
signin
|
42
|
-
signin.
|
79
|
+
describe '#signout' do
|
80
|
+
it 'should expire signin' do
|
81
|
+
sign_in_user(user, credentials)
|
82
|
+
signin = user.last_signin
|
83
|
+
sign_out_user(signin, credentials)
|
84
|
+
expect(signin.reload).to be_expired
|
43
85
|
end
|
44
86
|
|
45
|
-
context
|
46
|
-
%
|
47
|
-
it "
|
48
|
-
|
49
|
-
|
87
|
+
context 'when has no restrictions' do
|
88
|
+
%i[ip user_agent].each do |c|
|
89
|
+
it "allows signout when #{c} changes" do
|
90
|
+
sign_in_user(user, credentials)
|
91
|
+
signin = user.last_signin
|
92
|
+
expect(sign_out_user(signin, credentials)).to be_truthy
|
50
93
|
end
|
51
94
|
end
|
52
95
|
end
|
53
96
|
|
54
|
-
context
|
55
|
-
%
|
56
|
-
it "
|
57
|
-
|
58
|
-
|
59
|
-
|
97
|
+
context 'when has restrictions' do
|
98
|
+
%i[ip user_agent].each do |c|
|
99
|
+
it "forbids signout when #{c} changes" do
|
100
|
+
allow(described_class).to receive(:signin_restrictions).and_return([c])
|
101
|
+
sign_in_user(user, credentials)
|
102
|
+
signin = user.last_signin
|
103
|
+
expect(sign_out_user(signin, other_credentials)).to be_nil
|
60
104
|
end
|
61
105
|
end
|
62
106
|
end
|
63
107
|
end
|
64
108
|
|
65
|
-
describe
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
109
|
+
describe '#last_signin' do
|
110
|
+
it 'retuns nil when no signins' do
|
111
|
+
expect(user.last_signin).to be_nil
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'returns last active signin' do
|
115
|
+
sign_in_user(user, credentials)
|
116
|
+
sign_in_user(user, credentials)
|
117
|
+
signin = user.signins.active.last
|
118
|
+
sign_in_user(user, credentials)
|
119
|
+
user.signins.last.expire!
|
120
|
+
|
121
|
+
expect(user.last_signin).to eq(signin)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
describe '.generate_jwt' do
|
126
|
+
let(:token) { SecureRandom.urlsafe_base64(rand(50..100)) }
|
127
|
+
|
128
|
+
it 'sets correct payload' do
|
129
|
+
jwt = described_class.generate_jwt(token, user.id)
|
130
|
+
payload = JWT.decode(jwt, described_class.jwt_secret, true, { algorithm: 'HS256' })[0]
|
131
|
+
expect(payload).to eq(
|
132
|
+
'refresh_token' => token,
|
133
|
+
'signinable_id' => user.id,
|
134
|
+
'exp' => Time.zone.now.to_i + described_class.jwt_exp
|
135
|
+
)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
describe '.authenticate_with_token' do
|
140
|
+
context 'when jwt is invalid' do
|
141
|
+
it 'returns nil' do
|
142
|
+
expect(described_class.authenticate_with_token('blablabla', *credentials)).to be_nil
|
75
143
|
end
|
144
|
+
end
|
76
145
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
146
|
+
context 'when jwt has not expired' do
|
147
|
+
before(:each) do
|
148
|
+
sign_in_user(user, credentials)
|
149
|
+
end
|
150
|
+
|
151
|
+
it 'returns nil if user not found' do
|
152
|
+
allow(described_class).to receive(:find_by).and_return(nil)
|
153
|
+
expect(described_class.authenticate_with_token(user.jwt, *credentials)).to be_nil
|
154
|
+
end
|
155
|
+
|
156
|
+
it 'returns user' do
|
157
|
+
expect(described_class.authenticate_with_token(user.jwt, *credentials)).to eq(user)
|
158
|
+
end
|
159
|
+
|
160
|
+
it 'returns jwt with user' do
|
161
|
+
expect(described_class.authenticate_with_token(user.jwt, *credentials).jwt).to eq(user.jwt)
|
162
|
+
end
|
163
|
+
|
164
|
+
it 'does not update refresh token' do
|
165
|
+
allow(described_class).to receive(:refresh_jwt)
|
166
|
+
described_class.authenticate_with_token(user.jwt, *credentials)
|
167
|
+
expect(described_class).not_to have_received(:refresh_jwt)
|
85
168
|
end
|
86
169
|
end
|
87
170
|
|
88
|
-
context
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
User.authenticate_with_token(signin1.token, *@credentials).should eq(@user)
|
93
|
-
User.authenticate_with_token(signin2.token, *@credentials).should eq(@user)
|
171
|
+
context 'when jwt has expired' do
|
172
|
+
before(:each) do
|
173
|
+
sign_in_user(user, credentials)
|
174
|
+
Timecop.travel(Time.zone.now + described_class.jwt_exp)
|
94
175
|
end
|
95
176
|
|
96
|
-
it
|
97
|
-
|
98
|
-
|
177
|
+
it 'does not do user lookup' do
|
178
|
+
allow(described_class).to receive(:find_by)
|
179
|
+
described_class.authenticate_with_token(user.jwt, *credentials)
|
180
|
+
expect(described_class).not_to have_received(:find_by)
|
99
181
|
end
|
100
182
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
end
|
183
|
+
it 'calls for refresh token' do
|
184
|
+
allow(described_class).to receive(:refresh_jwt)
|
185
|
+
described_class.authenticate_with_token(user.jwt, *credentials)
|
186
|
+
expect(described_class).to have_received(:refresh_jwt)
|
106
187
|
end
|
107
188
|
end
|
189
|
+
end
|
108
190
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
signin2 = sign_in_user(@user, @credentials)
|
114
|
-
User.authenticate_with_token(signin1.token, *@credentials).should be_nil
|
115
|
-
User.authenticate_with_token(signin2.token, *@credentials).should eq(@user)
|
191
|
+
describe '.refresh_jwt' do
|
192
|
+
context 'when jwt is invalid' do
|
193
|
+
it 'returns nil' do
|
194
|
+
expect(described_class.refresh_jwt('blablabla', *credentials)).to be_nil
|
116
195
|
end
|
196
|
+
end
|
117
197
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
198
|
+
it 'returns nil when signin not found' do
|
199
|
+
jwt = described_class.generate_jwt('blablabla', 0)
|
200
|
+
expect(described_class.refresh_jwt(jwt, *credentials)).to be_nil
|
201
|
+
end
|
202
|
+
|
203
|
+
it 'returns nil when signin expired' do
|
204
|
+
sign_in_user(user, credentials)
|
205
|
+
signin = user.last_signin
|
206
|
+
Timecop.travel(Time.zone.now + described_class.refresh_exp)
|
207
|
+
expect(described_class.refresh_jwt(user.jwt, *credentials)).to be_nil
|
208
|
+
end
|
209
|
+
|
210
|
+
context 'when has no restrictions' do
|
211
|
+
%i[ip user_agent].each do |c|
|
212
|
+
it "allows signin when #{c} changed" do
|
213
|
+
sign_in_user(user, credentials)
|
214
|
+
expect(described_class.refresh_jwt(user.jwt, *other_credentials)).to eq(user)
|
215
|
+
end
|
122
216
|
end
|
217
|
+
end
|
123
218
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
219
|
+
context 'when has restrictions' do
|
220
|
+
%i[ip user_agent].each do |c|
|
221
|
+
it "forbids signin when #{c} changed" do
|
222
|
+
allow(User).to receive(:signin_restrictions).and_return([c])
|
223
|
+
sign_in_user(user, credentials)
|
224
|
+
expect(described_class.refresh_jwt(user.jwt, *other_credentials)).to be_nil
|
129
225
|
end
|
130
226
|
end
|
131
227
|
end
|
228
|
+
|
229
|
+
it 'renews signin' do
|
230
|
+
sign_in_user(user, credentials)
|
231
|
+
signin = user.last_signin
|
232
|
+
allow(signin).to receive(:renew!)
|
233
|
+
allow(Signin).to receive(:find_by).with(token: signin.token).and_return(signin)
|
234
|
+
|
235
|
+
described_class.refresh_jwt(user.jwt, *credentials)
|
236
|
+
expect(signin).to have_received(:renew!).with(period: described_class.expiration_period, ip: credentials[0],
|
237
|
+
user_agent: credentials[1], refresh_token: true)
|
238
|
+
end
|
239
|
+
|
240
|
+
it 'assigns new jwt' do
|
241
|
+
sign_in_user(user, credentials)
|
242
|
+
signin = user.last_signin
|
243
|
+
allow(user).to receive(:jwt=)
|
244
|
+
allow(signin).to receive(:signinable).and_return(user)
|
245
|
+
allow(Signin).to receive(:find_by).with(token: signin.token).and_return(signin)
|
246
|
+
allow(described_class).to receive(:generate_jwt).and_return('bla')
|
247
|
+
|
248
|
+
described_class.refresh_jwt(user.jwt, *credentials)
|
249
|
+
expect(user).to have_received(:jwt=).with('bla')
|
250
|
+
end
|
251
|
+
|
252
|
+
it 'regenerates jwt' do
|
253
|
+
sign_in_user(user, credentials)
|
254
|
+
signin = user.last_signin
|
255
|
+
allow(described_class).to receive(:generate_jwt)
|
256
|
+
|
257
|
+
described_class.refresh_jwt(user.jwt, *credentials)
|
258
|
+
signin.reload
|
259
|
+
expect(described_class).to have_received(:generate_jwt).with(signin.token, signin.signinable_id)
|
260
|
+
end
|
132
261
|
end
|
133
262
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
ENV['RAILS_ENV'] ||= 'test'
|
4
|
+
|
5
|
+
require File.expand_path('dummy/config/environment', __dir__)
|
6
|
+
|
7
|
+
require 'spec_helper'
|
8
|
+
require 'rspec/rails'
|
9
|
+
|
10
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].sort.each { |f| require f }
|
11
|
+
|
12
|
+
RSpec.configure do |config|
|
13
|
+
config.use_transactional_fixtures = true
|
14
|
+
|
15
|
+
config.infer_spec_type_from_file_location!
|
16
|
+
|
17
|
+
config.filter_rails_from_backtrace!
|
18
|
+
|
19
|
+
config.include FactoryBot::Syntax::Methods
|
20
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,18 +1,17 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require '
|
5
|
-
require 'rspec/autorun'
|
6
|
-
require 'factory_girl_rails'
|
3
|
+
require 'factory_bot_rails'
|
4
|
+
require 'pry'
|
7
5
|
require 'timecop'
|
8
6
|
|
9
|
-
|
7
|
+
RSpec.configure do |config|
|
8
|
+
config.expect_with :rspec do |expectations|
|
9
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
10
|
+
end
|
10
11
|
|
11
|
-
|
12
|
+
config.mock_with :rspec do |mocks|
|
13
|
+
mocks.verify_partial_doubles = true
|
14
|
+
end
|
12
15
|
|
13
|
-
|
14
|
-
config.mock_with :rspec
|
15
|
-
config.use_transactional_fixtures = false
|
16
|
-
config.infer_base_class_for_anonymous_controllers = false
|
17
|
-
config.order = 'random'
|
16
|
+
config.shared_context_metadata_behavior = :apply_to_host_groups
|
18
17
|
end
|
data/spec/support/utilities.rb
CHANGED