oauth2 1.4.4 → 1.4.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +41 -2
  3. data/CODE_OF_CONDUCT.md +105 -46
  4. data/LICENSE +1 -1
  5. data/README.md +277 -112
  6. data/lib/oauth2/access_token.rb +11 -8
  7. data/lib/oauth2/authenticator.rb +4 -2
  8. data/lib/oauth2/client.rb +131 -50
  9. data/lib/oauth2/error.rb +3 -1
  10. data/lib/oauth2/mac_token.rb +18 -10
  11. data/lib/oauth2/response.rb +7 -3
  12. data/lib/oauth2/strategy/assertion.rb +6 -4
  13. data/lib/oauth2/strategy/auth_code.rb +3 -1
  14. data/lib/oauth2/strategy/base.rb +2 -0
  15. data/lib/oauth2/strategy/client_credentials.rb +3 -1
  16. data/lib/oauth2/strategy/implicit.rb +3 -1
  17. data/lib/oauth2/strategy/password.rb +5 -3
  18. data/lib/oauth2/version.rb +9 -3
  19. data/lib/oauth2.rb +2 -0
  20. data/spec/fixtures/README.md +11 -0
  21. data/spec/fixtures/RS256/jwtRS256.key +51 -0
  22. data/spec/fixtures/RS256/jwtRS256.key.pub +14 -0
  23. data/spec/helper.rb +33 -0
  24. data/spec/oauth2/access_token_spec.rb +218 -0
  25. data/spec/oauth2/authenticator_spec.rb +86 -0
  26. data/spec/oauth2/client_spec.rb +556 -0
  27. data/spec/oauth2/mac_token_spec.rb +122 -0
  28. data/spec/oauth2/response_spec.rb +96 -0
  29. data/spec/oauth2/strategy/assertion_spec.rb +113 -0
  30. data/spec/oauth2/strategy/auth_code_spec.rb +108 -0
  31. data/spec/oauth2/strategy/base_spec.rb +7 -0
  32. data/spec/oauth2/strategy/client_credentials_spec.rb +71 -0
  33. data/spec/oauth2/strategy/implicit_spec.rb +28 -0
  34. data/spec/oauth2/strategy/password_spec.rb +58 -0
  35. data/spec/oauth2/version_spec.rb +23 -0
  36. metadata +54 -98
  37. data/.document +0 -5
  38. data/.gitignore +0 -19
  39. data/.jrubyrc +0 -1
  40. data/.rspec +0 -2
  41. data/.rubocop.yml +0 -80
  42. data/.rubocop_rspec.yml +0 -26
  43. data/.rubocop_todo.yml +0 -15
  44. data/.ruby-version +0 -1
  45. data/.travis.yml +0 -87
  46. data/CONTRIBUTING.md +0 -18
  47. data/Gemfile +0 -40
  48. data/Rakefile +0 -45
  49. data/gemfiles/jruby_1.7.gemfile +0 -11
  50. data/gemfiles/jruby_9.0.gemfile +0 -7
  51. data/gemfiles/jruby_9.1.gemfile +0 -3
  52. data/gemfiles/jruby_9.2.gemfile +0 -3
  53. data/gemfiles/jruby_head.gemfile +0 -3
  54. data/gemfiles/ruby_1.9.gemfile +0 -11
  55. data/gemfiles/ruby_2.0.gemfile +0 -6
  56. data/gemfiles/ruby_2.1.gemfile +0 -6
  57. data/gemfiles/ruby_2.2.gemfile +0 -3
  58. data/gemfiles/ruby_2.3.gemfile +0 -3
  59. data/gemfiles/ruby_2.4.gemfile +0 -3
  60. data/gemfiles/ruby_2.5.gemfile +0 -3
  61. data/gemfiles/ruby_2.6.gemfile +0 -9
  62. data/gemfiles/ruby_2.7.gemfile +0 -9
  63. data/gemfiles/ruby_head.gemfile +0 -9
  64. data/gemfiles/truffleruby.gemfile +0 -3
  65. data/oauth2.gemspec +0 -52
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe OAuth2::Response do
4
+ describe '#initialize' do
5
+ let(:status) { 200 }
6
+ let(:headers) { {'foo' => 'bar'} }
7
+ let(:body) { 'foo' }
8
+
9
+ it 'returns the status, headers and body' do
10
+ response = double('response', :headers => headers,
11
+ :status => status,
12
+ :body => body)
13
+ subject = described_class.new(response)
14
+ expect(subject.headers).to eq(headers)
15
+ expect(subject.status).to eq(status)
16
+ expect(subject.body).to eq(body)
17
+ end
18
+ end
19
+
20
+ describe '.register_parser' do
21
+ let(:response) do
22
+ double('response', :headers => {'Content-Type' => 'application/foo-bar'},
23
+ :status => 200,
24
+ :body => 'baz')
25
+ end
26
+
27
+ before do
28
+ described_class.register_parser(:foobar, 'application/foo-bar') do |body|
29
+ "foobar #{body}"
30
+ end
31
+ end
32
+
33
+ it 'adds to the content types and parsers' do
34
+ expect(described_class.send(:class_variable_get, :@@parsers).keys).to include(:foobar)
35
+ expect(described_class.send(:class_variable_get, :@@content_types).keys).to include('application/foo-bar')
36
+ end
37
+
38
+ it 'is able to parse that content type automatically' do
39
+ expect(described_class.new(response).parsed).to eq('foobar baz')
40
+ end
41
+ end
42
+
43
+ describe '#parsed' do
44
+ it 'parses application/x-www-form-urlencoded body' do
45
+ headers = {'Content-Type' => 'application/x-www-form-urlencoded'}
46
+ body = 'foo=bar&answer=42'
47
+ response = double('response', :headers => headers, :body => body)
48
+ subject = described_class.new(response)
49
+ expect(subject.parsed.keys.size).to eq(2)
50
+ expect(subject.parsed['foo']).to eq('bar')
51
+ expect(subject.parsed['answer']).to eq('42')
52
+ end
53
+
54
+ it 'parses application/json body' do
55
+ headers = {'Content-Type' => 'application/json'}
56
+ body = MultiJson.encode(:foo => 'bar', :answer => 42)
57
+ response = double('response', :headers => headers, :body => body)
58
+ subject = described_class.new(response)
59
+ expect(subject.parsed.keys.size).to eq(2)
60
+ expect(subject.parsed['foo']).to eq('bar')
61
+ expect(subject.parsed['answer']).to eq(42)
62
+ end
63
+
64
+ it "doesn't try to parse other content-types" do
65
+ headers = {'Content-Type' => 'text/html'}
66
+ body = '<!DOCTYPE html><html><head></head><body></body></html>'
67
+
68
+ response = double('response', :headers => headers, :body => body)
69
+
70
+ expect(MultiJson).not_to receive(:decode)
71
+ expect(MultiJson).not_to receive(:load)
72
+ expect(Rack::Utils).not_to receive(:parse_query)
73
+
74
+ subject = described_class.new(response)
75
+ expect(subject.parsed).to be_nil
76
+ end
77
+ end
78
+
79
+ context 'with xml parser registration' do
80
+ before do
81
+ MultiXml.parser = :rexml
82
+ end
83
+
84
+ it 'tries to load multi_xml and use it' do
85
+ expect(described_class.send(:class_variable_get, :@@parsers)[:xml]).not_to be_nil
86
+ end
87
+
88
+ it 'is able to parse xml' do
89
+ headers = {'Content-Type' => 'text/xml'}
90
+ body = '<?xml version="1.0" standalone="yes" ?><foo><bar>baz</bar></foo>'
91
+
92
+ response = double('response', :headers => headers, :body => body)
93
+ expect(described_class.new(response).parsed).to eq('foo' => {'bar' => 'baz'})
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'openssl'
4
+
5
+ describe OAuth2::Strategy::Assertion do
6
+ let(:client_assertion) { client.assertion }
7
+
8
+ let(:client) do
9
+ cli = OAuth2::Client.new('abc', 'def', :site => 'http://api.example.com')
10
+ cli.connection = Faraday.new(cli.site, cli.options[:connection_opts]) do |b|
11
+ b.request :url_encoded
12
+ b.adapter :test do |stub|
13
+ stub.post('/oauth/token') do |env|
14
+ case @mode
15
+ when 'formencoded'
16
+ [200, {'Content-Type' => 'application/x-www-form-urlencoded'}, 'expires_in=600&access_token=salmon&refresh_token=trout']
17
+ when 'json'
18
+ [200, {'Content-Type' => 'application/json'}, '{"expires_in":600,"access_token":"salmon","refresh_token":"trout"}']
19
+ end
20
+ end
21
+ end
22
+ end
23
+ cli
24
+ end
25
+
26
+ let(:params) do
27
+ {
28
+ :hmac_secret => 'foo',
29
+ :exp => Time.now.utc.to_i + 3600,
30
+ }
31
+ end
32
+
33
+ describe '#authorize_url' do
34
+ it 'raises NotImplementedError' do
35
+ expect { client_assertion.authorize_url }.to raise_error(NotImplementedError)
36
+ end
37
+ end
38
+
39
+ %w[json formencoded].each do |mode|
40
+ before { @mode = mode }
41
+
42
+ shared_examples_for "get_token #{mode}" do
43
+ describe "#get_token (#{mode})" do
44
+ subject(:get_token) { client_assertion.get_token(params) }
45
+
46
+ it 'returns AccessToken with same Client' do
47
+ expect(get_token.client).to eq(client)
48
+ end
49
+
50
+ it 'returns AccessToken with #token' do
51
+ expect(get_token.token).to eq('salmon')
52
+ end
53
+
54
+ it 'returns AccessToken with #expires_in' do
55
+ expect(get_token.expires_in).to eq(600)
56
+ end
57
+
58
+ it 'returns AccessToken with #expires_at' do
59
+ expect(get_token.expires_at).not_to be_nil
60
+ end
61
+ end
62
+ end
63
+
64
+ it_behaves_like "get_token #{mode}"
65
+ describe "#build_assertion (#{mode})" do
66
+ context 'with hmac_secret' do
67
+ subject(:build_assertion) { client_assertion.build_assertion(params) }
68
+
69
+ let(:hmac_secret) { '1883be842495c3b58f68ca71fbf1397fbb9ed2fdf8990f8404a25d0a1b995943' }
70
+ let(:params) do
71
+ {
72
+ :iss => 2345,
73
+ :aud => 'too',
74
+ :prn => 'much',
75
+ :exp => 123_456_789,
76
+ :hmac_secret => hmac_secret,
77
+ }
78
+ end
79
+ let(:jwt) { 'eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOjIzNDUsImF1ZCI6InRvbyIsInBybiI6Im11Y2giLCJleHAiOjEyMzQ1Njc4OX0.GnZjgcdc5WSWKNW0p9S4GuhpBs3LJCEqjPm6turLG-c' }
80
+
81
+ it 'returns JWT' do
82
+ expect(build_assertion).to eq(jwt)
83
+ end
84
+
85
+ it_behaves_like "get_token #{mode}"
86
+ end
87
+
88
+ context 'with private_key' do
89
+ subject(:build_assertion) { client_assertion.build_assertion(params) }
90
+
91
+ let(:private_key_file) { 'spec/fixtures/RS256/jwtRS256.key' }
92
+ let(:password) { '' }
93
+ let(:private_key) { OpenSSL::PKey::RSA.new(File.read(private_key_file), password) }
94
+ let(:params) do
95
+ {
96
+ :iss => 2345,
97
+ :aud => 'too',
98
+ :prn => 'much',
99
+ :exp => 123_456_789,
100
+ :private_key => private_key,
101
+ }
102
+ end
103
+ let(:jwt) { 'eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOjIzNDUsImF1ZCI6InRvbyIsInBybiI6Im11Y2giLCJleHAiOjEyMzQ1Njc4OX0.vJ32OiPVMdJrlNkPw02Y9u6beiFY0Mfndhg_CkEDLtOYn8dscQIEpWoR4GzH8tiQVOQ1fOkqxE95tNIKOTjnIoskmYnfzhzIl9fnfQ_lsEuLC-nq45KhPzSM2wYgF2ZEIjDq51daK70bRPzTBr1Id45cTY-jJSito0lbKXj2nPa_Gs-_vyEU2MSxjiMaIxxccfY4Ow5zN3AUMTKp6LjrpDKFxag3fJ1nrb6iDATa504gyJHVLift3ovhAwYidkA81WnmEtISWBY904CKIcZD9Cx3ifS5bc3JaLAteIBKAAyD8o7D60vOKutsjCMHUCKL357BQ36bW7fmaEtW367Ri-xgOsCY0_HeWp991vrJ-DxhFPeuF-8hn_9KggBzKbA2eKEOOY4iDKSFwjWQUFOcRdvHw9RgbGt0IjY3wdo8CaJVlhynh54YlaLgOFhTBPeMgZdqQUHOztljaK9zubeVkrDGNnGuSuq0KR82KArb1x2z7XyZpxiV5ZatP9SNyhn-YIWk7UeQYXaS0UfsBX7L5T1y_FZj84r7Vl42lj1DfdR5DyGvHfZyHotTnejdIrDuQfDL_bGe24eHsilzuEFaajYmu10hxflZ6Apm-lekRRV47tbxTF1zI5we14XsTeklrTXqgDkSw6gyOoNUJm-cQkJpfdvBgUHYGInC1ttz7NU' }
104
+
105
+ it 'returns JWT' do
106
+ expect(build_assertion).to eq(jwt)
107
+ end
108
+
109
+ it_behaves_like "get_token #{mode}"
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,108 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ describe OAuth2::Strategy::AuthCode do
5
+ subject { client.auth_code }
6
+
7
+ let(:code) { 'sushi' }
8
+ let(:kvform_token) { 'expires_in=600&access_token=salmon&refresh_token=trout&extra_param=steve' }
9
+ let(:facebook_token) { kvform_token.gsub('_in', '') }
10
+ let(:json_token) { MultiJson.encode(:expires_in => 600, :access_token => 'salmon', :refresh_token => 'trout', :extra_param => 'steve') }
11
+
12
+ let(:client) do
13
+ OAuth2::Client.new('abc', 'def', :site => 'http://api.example.com') do |builder|
14
+ builder.adapter :test do |stub|
15
+ stub.get("/oauth/token?client_id=abc&client_secret=def&code=#{code}&grant_type=authorization_code") do |env|
16
+ case @mode
17
+ when 'formencoded'
18
+ [200, {'Content-Type' => 'application/x-www-form-urlencoded'}, kvform_token]
19
+ when 'json'
20
+ [200, {'Content-Type' => 'application/json'}, json_token]
21
+ when 'from_facebook'
22
+ [200, {'Content-Type' => 'application/x-www-form-urlencoded'}, facebook_token]
23
+ end
24
+ end
25
+ stub.post('/oauth/token', 'client_id' => 'abc', 'client_secret' => 'def', 'code' => 'sushi', 'grant_type' => 'authorization_code') do |env|
26
+ case @mode
27
+ when 'formencoded'
28
+ [200, {'Content-Type' => 'application/x-www-form-urlencoded'}, kvform_token]
29
+ when 'json'
30
+ [200, {'Content-Type' => 'application/json'}, json_token]
31
+ when 'from_facebook'
32
+ [200, {'Content-Type' => 'application/x-www-form-urlencoded'}, facebook_token]
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ describe '#authorize_url' do
40
+ it 'includes the client_id' do
41
+ expect(subject.authorize_url).to include('client_id=abc')
42
+ end
43
+
44
+ it 'includes the type' do
45
+ expect(subject.authorize_url).to include('response_type=code')
46
+ end
47
+
48
+ it 'includes passed in options' do
49
+ cb = 'http://myserver.local/oauth/callback'
50
+ expect(subject.authorize_url(:redirect_uri => cb)).to include("redirect_uri=#{Rack::Utils.escape(cb)}")
51
+ end
52
+ end
53
+
54
+ describe '#get_token (handling utf-8 data)' do
55
+ let(:json_token) { MultiJson.encode(:expires_in => 600, :access_token => 'salmon', :refresh_token => 'trout', :extra_param => 'André') }
56
+
57
+ before do
58
+ @mode = 'json'
59
+ client.options[:token_method] = :post
60
+ end
61
+
62
+ it 'does not raise an error' do
63
+ expect { subject.get_token(code) }.not_to raise_error
64
+ end
65
+
66
+ it 'does not create an error instance' do
67
+ expect(OAuth2::Error).not_to receive(:new)
68
+
69
+ subject.get_token(code)
70
+ end
71
+ end
72
+
73
+ %w[json formencoded from_facebook].each do |mode|
74
+ [:get, :post].each do |verb|
75
+ describe "#get_token (#{mode}, access_token_method=#{verb}" do
76
+ before do
77
+ @mode = mode
78
+ client.options[:token_method] = verb
79
+ @access = subject.get_token(code)
80
+ end
81
+
82
+ it 'returns AccessToken with same Client' do
83
+ expect(@access.client).to eq(client)
84
+ end
85
+
86
+ it 'returns AccessToken with #token' do
87
+ expect(@access.token).to eq('salmon')
88
+ end
89
+
90
+ it 'returns AccessToken with #refresh_token' do
91
+ expect(@access.refresh_token).to eq('trout')
92
+ end
93
+
94
+ it 'returns AccessToken with #expires_in' do
95
+ expect(@access.expires_in).to eq(600)
96
+ end
97
+
98
+ it 'returns AccessToken with #expires_at' do
99
+ expect(@access.expires_at).to be_kind_of(Integer)
100
+ end
101
+
102
+ it 'returns AccessToken with params accessible via []' do
103
+ expect(@access['extra_param']).to eq('steve')
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe OAuth2::Strategy::Base do
4
+ it 'initializes with a Client' do
5
+ expect { described_class.new(OAuth2::Client.new('abc', 'def')) }.not_to raise_error
6
+ end
7
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe OAuth2::Strategy::ClientCredentials do
4
+ subject { client.client_credentials }
5
+
6
+ let(:kvform_token) { 'expires_in=600&access_token=salmon&refresh_token=trout' }
7
+ let(:json_token) { '{"expires_in":600,"access_token":"salmon","refresh_token":"trout"}' }
8
+
9
+ let(:client) do
10
+ OAuth2::Client.new('abc', 'def', :site => 'http://api.example.com') do |builder|
11
+ builder.adapter :test do |stub|
12
+ stub.post('/oauth/token', 'grant_type' => 'client_credentials') do |env|
13
+ client_id, client_secret = Base64.decode64(env[:request_headers]['Authorization'].split(' ', 2)[1]).split(':', 2)
14
+ (client_id == 'abc' && client_secret == 'def') || raise(Faraday::Adapter::Test::Stubs::NotFound)
15
+ case @mode
16
+ when 'formencoded'
17
+ [200, {'Content-Type' => 'application/x-www-form-urlencoded'}, kvform_token]
18
+ when 'json'
19
+ [200, {'Content-Type' => 'application/json'}, json_token]
20
+ end
21
+ end
22
+ stub.post('/oauth/token', 'client_id' => 'abc', 'client_secret' => 'def', 'grant_type' => 'client_credentials') do |env|
23
+ case @mode
24
+ when 'formencoded'
25
+ [200, {'Content-Type' => 'application/x-www-form-urlencoded'}, kvform_token]
26
+ when 'json'
27
+ [200, {'Content-Type' => 'application/json'}, json_token]
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ describe '#authorize_url' do
35
+ it 'raises NotImplementedError' do
36
+ expect { subject.authorize_url }.to raise_error(NotImplementedError)
37
+ end
38
+ end
39
+
40
+ %w[json formencoded].each do |mode|
41
+ [:basic_auth, :request_body].each do |auth_scheme|
42
+ describe "#get_token (#{mode}) (#{auth_scheme})" do
43
+ before do
44
+ @mode = mode
45
+ client.options[:auth_scheme] = auth_scheme
46
+ @access = subject.get_token
47
+ end
48
+
49
+ it 'returns AccessToken with same Client' do
50
+ expect(@access.client).to eq(client)
51
+ end
52
+
53
+ it 'returns AccessToken with #token' do
54
+ expect(@access.token).to eq('salmon')
55
+ end
56
+
57
+ it 'returns AccessToken without #refresh_token' do
58
+ expect(@access.refresh_token).to be_nil
59
+ end
60
+
61
+ it 'returns AccessToken with #expires_in' do
62
+ expect(@access.expires_in).to eq(600)
63
+ end
64
+
65
+ it 'returns AccessToken with #expires_at' do
66
+ expect(@access.expires_at).not_to be_nil
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe OAuth2::Strategy::Implicit do
4
+ subject { client.implicit }
5
+
6
+ let(:client) { OAuth2::Client.new('abc', 'def', :site => 'http://api.example.com') }
7
+
8
+ describe '#authorize_url' do
9
+ it 'includes the client_id' do
10
+ expect(subject.authorize_url).to include('client_id=abc')
11
+ end
12
+
13
+ it 'includes the type' do
14
+ expect(subject.authorize_url).to include('response_type=token')
15
+ end
16
+
17
+ it 'includes passed in options' do
18
+ cb = 'http://myserver.local/oauth/callback'
19
+ expect(subject.authorize_url(:redirect_uri => cb)).to include("redirect_uri=#{Rack::Utils.escape(cb)}")
20
+ end
21
+ end
22
+
23
+ describe '#get_token' do
24
+ it 'raises NotImplementedError' do
25
+ expect { subject.get_token }.to raise_error(NotImplementedError)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe OAuth2::Strategy::Password do
4
+ subject { client.password }
5
+
6
+ let(:client) do
7
+ cli = OAuth2::Client.new('abc', 'def', :site => 'http://api.example.com')
8
+ cli.connection = Faraday.new(cli.site, cli.options[:connection_opts]) do |b|
9
+ b.request :url_encoded
10
+ b.adapter :test do |stub|
11
+ stub.post('/oauth/token') do |env|
12
+ case @mode
13
+ when 'formencoded'
14
+ [200, {'Content-Type' => 'application/x-www-form-urlencoded'}, 'expires_in=600&access_token=salmon&refresh_token=trout']
15
+ when 'json'
16
+ [200, {'Content-Type' => 'application/json'}, '{"expires_in":600,"access_token":"salmon","refresh_token":"trout"}']
17
+ end
18
+ end
19
+ end
20
+ end
21
+ cli
22
+ end
23
+
24
+ describe '#authorize_url' do
25
+ it 'raises NotImplementedError' do
26
+ expect { subject.authorize_url }.to raise_error(NotImplementedError)
27
+ end
28
+ end
29
+
30
+ %w[json formencoded].each do |mode|
31
+ describe "#get_token (#{mode})" do
32
+ before do
33
+ @mode = mode
34
+ @access = subject.get_token('username', 'password')
35
+ end
36
+
37
+ it 'returns AccessToken with same Client' do
38
+ expect(@access.client).to eq(client)
39
+ end
40
+
41
+ it 'returns AccessToken with #token' do
42
+ expect(@access.token).to eq('salmon')
43
+ end
44
+
45
+ it 'returns AccessToken with #refresh_token' do
46
+ expect(@access.refresh_token).to eq('trout')
47
+ end
48
+
49
+ it 'returns AccessToken with #expires_in' do
50
+ expect(@access.expires_in).to eq(600)
51
+ end
52
+
53
+ it 'returns AccessToken with #expires_at' do
54
+ expect(@access.expires_at).not_to be_nil
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe OAuth2::Version do
4
+ it 'has a version number' do
5
+ expect(described_class).not_to be nil
6
+ end
7
+
8
+ it 'can be a string' do
9
+ expect(described_class.to_s).to be_a(String)
10
+ end
11
+
12
+ it 'allows Constant access' do
13
+ expect(described_class::VERSION).to be_a(String)
14
+ end
15
+
16
+ it 'is greater than 0.1.0' do
17
+ expect(Gem::Version.new(described_class) > Gem::Version.new('0.1.0')).to be(true)
18
+ end
19
+
20
+ it 'is not a pre-release' do
21
+ expect(Gem::Version.new(described_class).prerelease?).to be(false)
22
+ end
23
+ end