omniauth-identity 3.0.4 → 3.0.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- RSpec.describe(OmniAuth::Identity::Models::ActiveRecord, db: true) do
3
+ require 'sqlite3'
4
+ require 'active_record'
5
+ require 'anonymous_active_record'
6
+
7
+ class TestIdentity < OmniAuth::Identity::Models::ActiveRecord; end
8
+
9
+ RSpec.describe(OmniAuth::Identity::Models::ActiveRecord, sqlite3: true) do
4
10
  describe 'model', type: :model do
5
11
  subject(:model_klass) do
6
12
  AnonymousActiveRecord.generate(
@@ -8,23 +14,27 @@ RSpec.describe(OmniAuth::Identity::Models::ActiveRecord, db: true) do
8
14
  columns: OmniAuth::Identity::Model::SCHEMA_ATTRIBUTES | %w[provider password_digest],
9
15
  connection_params: { adapter: 'sqlite3', encoding: 'utf8', database: ':memory:' }
10
16
  ) do
17
+ auth_key :email
11
18
  def flower
12
19
  '🌸'
13
20
  end
14
21
  end
15
22
  end
16
23
 
17
- it 'delegates locate to the where query method' do
18
- allow(model_klass).to receive(:where).with('ham_sandwich' => 'open faced', 'category' => 'sandwiches',
19
- 'provider' => 'identity').and_return(['wakka'])
20
- expect(model_klass.locate('ham_sandwich' => 'open faced', 'category' => 'sandwiches')).to eq('wakka')
24
+ include_context 'persistable model'
25
+
26
+ describe '::table_name' do
27
+ it 'does not use STI rules for its table name' do
28
+ expect(TestIdentity.table_name).to eq('test_identities')
29
+ end
21
30
  end
22
- end
23
31
 
24
- describe '#table_name' do
25
- class TestIdentity < OmniAuth::Identity::Models::ActiveRecord; end
26
- it 'does not use STI rules for its table name' do
27
- expect(TestIdentity.table_name).to eq('test_identities')
32
+ describe '::locate' do
33
+ it 'delegates locate to the where query method' do
34
+ allow(model_klass).to receive(:where).with('email' => 'open faced', 'category' => 'sandwiches',
35
+ 'provider' => 'identity').and_return(['wakka'])
36
+ expect(model_klass.locate('email' => 'open faced', 'category' => 'sandwiches')).to eq('wakka')
37
+ end
28
38
  end
29
39
  end
30
40
  end
@@ -1,23 +1,39 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'sqlite3'
3
4
  require 'sequel'
4
- # Connect to an in-memory sqlite3 database.
5
+
5
6
  DB = Sequel.sqlite
6
- DB.create_table :sequel_test_identities do
7
- primary_key :id
8
- String :ham_sandwich, null: false
9
- String :password_digest, null: false
10
- end
11
7
 
12
- class SequelTestIdentity < Sequel::Model
13
- include OmniAuth::Identity::Models::Sequel
14
- auth_key :ham_sandwich
15
- end
8
+ RSpec.describe(OmniAuth::Identity::Models::Sequel, sqlite3: true) do
9
+ before(:all) do
10
+ # Connect to an in-memory sqlite3 database.
11
+ DB.create_table :sequel_test_identities do
12
+ primary_key :id
13
+ String :email, null: false
14
+ String :password_digest, null: false
15
+ end
16
+ end
17
+
18
+ before do
19
+ sequel_test_identity = Class.new(Sequel::Model(:sequel_test_identities)) do
20
+ include ::OmniAuth::Identity::Models::Sequel
21
+ auth_key :email
22
+ end
23
+ stub_const('SequelTestIdentity', sequel_test_identity)
24
+ end
25
+
26
+ describe 'model', type: :model do
27
+ subject(:model_klass) { SequelTestIdentity }
28
+
29
+ include_context 'persistable model'
16
30
 
17
- RSpec.describe(OmniAuth::Identity::Models::Sequel, db: true) do
18
- it 'delegates locate to the where query method' do
19
- allow(SequelTestIdentity).to receive(:where).with('ham_sandwich' => 'open faced',
20
- 'category' => 'sandwiches').and_return(['wakka'])
21
- expect(SequelTestIdentity.locate('ham_sandwich' => 'open faced', 'category' => 'sandwiches')).to eq('wakka')
31
+ describe '::locate' do
32
+ it 'delegates to the where query method' do
33
+ allow(model_klass).to receive(:where).with('email' => 'open faced',
34
+ 'category' => 'sandwiches').and_return(['wakka'])
35
+ expect(model_klass.locate('email' => 'open faced', 'category' => 'sandwiches')).to eq('wakka')
36
+ end
37
+ end
22
38
  end
23
39
  end
@@ -1,10 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- RSpec.describe OmniAuth::Strategies::Identity do
3
+ require 'sqlite3'
4
+ require 'active_record'
5
+ require 'anonymous_active_record'
6
+
7
+ RSpec.describe OmniAuth::Strategies::Identity, sqlite3: true do
4
8
  attr_accessor :app
5
9
 
6
- let(:auth_hash) { last_response.headers['env']['omniauth.auth'] }
7
- let(:identity_hash) { last_response.headers['env']['omniauth.identity'] }
10
+ let(:env_hash) { last_response.headers['env'] }
11
+ let(:auth_hash) { env_hash['omniauth.auth'] }
12
+ let(:identity_hash) { env_hash['omniauth.identity'] }
8
13
  let(:identity_options) { {} }
9
14
  let(:anon_ar) do
10
15
  AnonymousActiveRecord.generate(
@@ -12,6 +17,7 @@ RSpec.describe OmniAuth::Strategies::Identity do
12
17
  columns: OmniAuth::Identity::Model::SCHEMA_ATTRIBUTES | %w[provider password_digest],
13
18
  connection_params: { adapter: 'sqlite3', encoding: 'utf8', database: ':memory:' }
14
19
  ) do
20
+ auth_key :email
15
21
  def balloon
16
22
  '🎈'
17
23
  end
@@ -193,7 +199,7 @@ RSpec.describe OmniAuth::Strategies::Identity do
193
199
  end
194
200
  end
195
201
 
196
- context 'with successful creation' do
202
+ context 'with good identity' do
197
203
  let(:properties) do
198
204
  {
199
205
  name: 'Awesome Dude',
@@ -209,9 +215,66 @@ RSpec.describe OmniAuth::Strategies::Identity do
209
215
  expect(auth_hash['uid']).to match(/\d+/)
210
216
  expect(auth_hash['provider']).to eq('identity')
211
217
  end
218
+
219
+ context 'with on_validation proc' do
220
+ let(:identity_options) do
221
+ { model: anon_ar, on_validation: on_validation_proc }
222
+ end
223
+ let(:on_validation_proc) do
224
+ lambda { |_env|
225
+ false
226
+ }
227
+ end
228
+
229
+ context 'when validation fails' do
230
+ it 'does not set the env hash' do
231
+ post '/auth/identity/register', properties
232
+ expect(env_hash).to eq(nil)
233
+ end
234
+
235
+ it 'renders registration form' do
236
+ post '/auth/identity/register', properties
237
+ expect(last_response.body).to be_include(described_class.default_options[:registration_form_title])
238
+ end
239
+
240
+ it 'displays validation failure message' do
241
+ post '/auth/identity/register', properties
242
+ expect(last_response.body).to be_include(described_class.default_options[:validation_failure_message])
243
+ end
244
+ end
245
+
246
+ context 'when validation succeeds' do
247
+ let(:on_validation_proc) do
248
+ lambda { |_env|
249
+ true
250
+ }
251
+ end
252
+
253
+ it 'sets the auth hash' do
254
+ post '/auth/identity/register', properties
255
+ expect(auth_hash['uid']).to match(/\d+/)
256
+ expect(auth_hash['provider']).to eq('identity')
257
+ end
258
+
259
+ it 'does not render registration form' do
260
+ post '/auth/identity/register', properties
261
+ expect(last_response.body).not_to be_include(described_class.default_options[:registration_form_title])
262
+ end
263
+
264
+ it 'does not display validation failure message' do
265
+ post '/auth/identity/register', properties
266
+ expect(last_response.body).not_to be_include(described_class.default_options[:validation_failure_message])
267
+ end
268
+
269
+ it 'does not display registration failure message' do
270
+ post '/auth/identity/register', properties
271
+ expect(last_response.body).not_to be_include(described_class.default_options[:registration_failure_message])
272
+ end
273
+ end
274
+ end
212
275
  end
213
276
 
214
- context 'with invalid identity' do
277
+ context 'with bad identity' do
215
278
  let(:properties) do
216
279
  {
217
280
  name: 'Awesome Dude',
@@ -249,6 +312,62 @@ RSpec.describe OmniAuth::Strategies::Identity do
249
312
  expect(last_response.body).not_to be_include('One or more fields were invalid')
250
313
  end
251
314
  end
315
+
316
+ context 'with on_validation proc' do
317
+ let(:identity_options) do
318
+ { model: anon_ar, on_validation: on_validation_proc }
319
+ end
320
+ let(:on_validation_proc) do
321
+ lambda { |_env|
322
+ false
323
+ }
324
+ end
325
+
326
+ context 'when validation fails' do
327
+ it 'does not set the env hash' do
328
+ post '/auth/identity/register', properties
329
+ expect(env_hash).to eq(nil)
330
+ end
331
+
332
+ it 'renders registration form' do
333
+ post '/auth/identity/register', properties
334
+ expect(last_response.body).to be_include(described_class.default_options[:registration_form_title])
335
+ end
336
+
337
+ it 'displays validation failure message' do
338
+ post '/auth/identity/register', properties
339
+ expect(last_response.body).to be_include(described_class.default_options[:validation_failure_message])
340
+ end
341
+ end
342
+
343
+ context 'when validation succeeds' do
344
+ let(:on_validation_proc) do
345
+ lambda { |_env|
346
+ true
347
+ }
348
+ end
349
+
350
+ it 'does not set the env hash' do
351
+ post '/auth/identity/register', properties
352
+ expect(env_hash).to eq(nil)
353
+ end
354
+
355
+ it 'renders registration form' do
356
+ post '/auth/identity/register', properties
357
+ expect(last_response.body).to be_include(described_class.default_options[:registration_form_title])
358
+ end
359
+
360
+ it 'does not display validation failure message' do
361
+ post '/auth/identity/register', properties
362
+ expect(last_response.body).not_to be_include(described_class.default_options[:validation_failure_message])
363
+ end
364
+
365
+ it 'display registration failure message' do
366
+ post '/auth/identity/register', properties
367
+ expect(last_response.body).to be_include(described_class.default_options[:registration_failure_message])
368
+ end
369
+ end
370
+ end
252
371
  end
253
372
  end
254
373
  end
data/spec/spec_helper.rb CHANGED
@@ -1,21 +1,32 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # NOTE: mongoid and no_brainer can't be loaded at the same time.
4
+ # If you try it, one or both of them will not work.
5
+ # This is why the ORM specs are split into a separate directory and run in separate threads.
6
+
7
+ ENV['RUBY_ENV'] = 'test' # Used by NoBrainer
8
+ ENV['MONGOID_ENV'] = 'test' # Used by Mongoid
9
+
3
10
  ruby_version = Gem::Version.new(RUBY_VERSION)
4
11
  require 'simplecov' if ruby_version >= Gem::Version.new('2.7') && RUBY_ENGINE == 'ruby'
5
12
 
6
13
  require 'rack/test'
7
- require 'mongoid-rspec'
8
- require 'sqlite3'
9
- require 'sequel'
10
- require 'anonymous_active_record'
14
+ require 'rspec/block_is_expected'
11
15
  require 'byebug' if RUBY_ENGINE == 'ruby'
12
16
 
13
17
  # This gem
14
18
  require 'omniauth/identity'
15
19
 
20
+ spec_root_matcher = %r{#{__dir__}/(.+)\.rb\Z}
21
+ Dir.glob(Pathname.new(__dir__).join('support/**/', '*.rb')).each { |f| require f.match(spec_root_matcher)[1] }
22
+
23
+ DEFAULT_PASSWORD = 'hang-a-left-at-the-diner'
24
+ DEFAULT_EMAIL = 'mojo@example.com'
25
+
16
26
  RSpec.configure do |config|
17
27
  config.include Rack::Test::Methods
18
- config.include Mongoid::Matchers, type: :model
28
+
29
+ # config.include ::Mongoid::Matchers, db: :mongodb
19
30
 
20
31
  # Enable flags like --only-failures and --next-failure
21
32
  config.example_status_persistence_file_path = '.rspec_status'
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.shared_examples 'instance with instance methods' do
4
+ describe '#initialize' do
5
+ it 'does not raise an error' do
6
+ block_is_expected.not_to raise_error
7
+ end
8
+ end
9
+
10
+ describe '#uid' do
11
+ it 'defaults to #id' do
12
+ allow(instance).to receive(:respond_to?).with(:id).and_return(true)
13
+ allow(instance).to receive(:id).and_return 'wakka-do'
14
+ expect(instance.uid).to eq('wakka-do')
15
+ end
16
+
17
+ it 'stringifies it' do
18
+ allow(instance).to receive(:id).and_return 123
19
+ expect(instance.uid).to eq('123')
20
+ end
21
+
22
+ it 'raises NotImplementedError if #id is not defined' do
23
+ allow(instance).to receive(:respond_to?).with(:id).and_return(false)
24
+ expect { instance.uid }.to raise_error(NotImplementedError)
25
+ end
26
+ end
27
+
28
+ describe '#auth_key' do
29
+ it 'defaults to #email' do
30
+ allow(instance).to receive(:respond_to?).with(:email).and_return(true)
31
+ allow(instance).to receive(:email).and_return('bob@bob.com')
32
+ expect(instance.auth_key).to eq('bob@bob.com')
33
+ end
34
+
35
+ it 'uses the class .auth_key' do
36
+ instance.class.auth_key 'login'
37
+ allow(instance).to receive(:login).and_return 'bob'
38
+ expect(instance.auth_key).to eq('bob')
39
+ instance.class.auth_key nil
40
+ end
41
+ end
42
+
43
+ describe '#auth_key=' do
44
+ it 'defaults to setting email' do
45
+ allow(instance).to receive(:respond_to?).with(:email=).and_return(true)
46
+ expect(instance).to receive(:email=).with 'abc'
47
+
48
+ instance.auth_key = 'abc'
49
+ end
50
+
51
+ it 'uses a custom .auth_key if one is provided' do
52
+ instance.class.auth_key 'login'
53
+ allow(instance).to receive(:respond_to?).with(:login=).and_return(true)
54
+ expect(instance).to receive(:login=).with('abc')
55
+
56
+ instance.auth_key = 'abc'
57
+ end
58
+ end
59
+
60
+ describe '#info' do
61
+ it 'includes all attributes as they have been set' do
62
+ allow(instance).to receive(:name).and_return('Bob Bobson')
63
+ allow(instance).to receive(:nickname).and_return('bob')
64
+
65
+ expect(instance.info).to include({
66
+ 'name' => 'Bob Bobson',
67
+ 'nickname' => 'bob'
68
+ })
69
+ end
70
+
71
+ it 'uses firstname and lastname, over nickname, to set missing name' do
72
+ allow(instance).to receive(:first_name).and_return('shoeless')
73
+ allow(instance).to receive(:last_name).and_return('joe')
74
+ allow(instance).to receive(:nickname).and_return('george')
75
+ instance.info['name'] == 'shoeless joe'
76
+ end
77
+
78
+ it 'uses nickname to set missing name when first and last are not set' do
79
+ allow(instance).to receive(:nickname).and_return('bob')
80
+ instance.info['name'] == 'bob'
81
+ end
82
+
83
+ it 'does not overwrite a provided name' do
84
+ allow(instance).to receive(:name).and_return('Awesome Dude')
85
+ allow(instance).to receive(:first_name).and_return('Frank')
86
+ expect(instance.info['name']).to eq('Awesome Dude')
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.shared_examples 'model with class methods' do
4
+ describe 'class definition' do
5
+ it 'does not raise an error' do
6
+ block_is_expected.not_to raise_error
7
+ end
8
+ end
9
+
10
+ describe '::authenticate' do
11
+ it 'calls locate and then authenticate' do
12
+ mocked_instance = double('ExampleModel', authenticate: 'abbadoo')
13
+ allow(model_klass).to receive(:locate).with('email' => 'example').and_return(mocked_instance)
14
+ expect(model_klass.authenticate({ 'email' => 'example' }, 'pass')).to eq('abbadoo')
15
+ end
16
+
17
+ it 'calls locate with additional scopes when provided' do
18
+ mocked_instance = double('ExampleModel', authenticate: 'abbadoo')
19
+ allow(model_klass).to receive(:locate).with('email' => 'example',
20
+ 'user_type' => 'admin').and_return(mocked_instance)
21
+ expect(model_klass.authenticate({ 'email' => 'example', 'user_type' => 'admin' }, 'pass')).to eq('abbadoo')
22
+ end
23
+
24
+ it 'recovers gracefully if locate is nil' do
25
+ allow(model_klass).to receive(:locate).and_return(nil)
26
+ expect(model_klass.authenticate('blah', 'foo')).to be false
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.shared_context 'persistable model' do
4
+ include_context 'model with class methods'
5
+
6
+ describe 'instance methods' do
7
+ subject(:instance) { model_klass.new }
8
+
9
+ include_context 'instance with instance methods'
10
+
11
+ describe '#save' do
12
+ subject(:save) do
13
+ instance.email = DEFAULT_EMAIL
14
+ instance.password = DEFAULT_PASSWORD
15
+ instance.password_confirmation = DEFAULT_PASSWORD
16
+ instance.save
17
+ end
18
+
19
+ it 'does not raise an error' do
20
+ save
21
+ end
22
+ end
23
+ end
24
+ end